İşlev işaretçisi tanımları neden herhangi bir sayıda '&' veya yıldız işareti '*' ile çalışır?


216

Aşağıdakiler neden işe yarıyor?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}

Yanıtlar:


224

Tüm bu operatör kombinasyonlarının aynı şekilde çalışmasına izin veren birkaç parça var.

Tüm bu çalışmaların neden bir fonksiyonun (gibi foo) dolaylı olarak fonksiyonun bir göstergesine dönüştürülebilir olmasıdır. Bu yüzden void (*p1_foo)() = foo;çalışır: foodolaylı olarak kendisine bir işaretçiye dönüştürülür ve bu işaretçiye atanır p1_foo.

Tekli &, bir işleve uygulandığında, bir nesneye uygulandığında bir nesnenin adresini verdiği gibi, işleve bir işaretçi verir. Sıradan işlevlere işaretçiler için, örtük işlevden işleve işaretçi dönüşümü nedeniyle her zaman gereksizdir. Her durumda, bu yüzden void (*p3_foo)() = &foo;çalışıyor.

Tekli *, bir işlev işaretçisine uygulandığında, tıpkı bir nesneye normal bir işaretçiye uygulandığında sivri uçlu nesneye verdiği gibi, sivri uçlu işlevi verir.

Bu kurallar birleştirilebilir. Sondan ikinci örneğe bakalım **foo:

  • Birincisi, foodolaylı olarak kendisine bir işaretçiye dönüştürülür ve birincisi *bu işlev işaretçisine uygulanır ve işlevi fooyeniden verir.
  • Daha sonra, sonuç tekrar örtük olarak kendisine bir göstergeye dönüştürülür ve ikincisi *uygulanır, yine işlevi verir foo.
  • Daha sonra, dolaylı olarak tekrar bir işlev işaretçisine dönüştürülür ve değişkene atanır.

İstediğiniz kadar ekleyebilirsiniz *, sonuç her zaman aynıdır. Daha fazla *, daha iyi .

Beşinci örneğinizi de düşünebiliriz &*foo:

  • İlk olarak, fooörtük olarak kendisine bir işaretçi haline dönüştürülür; unary tekrar *uygulanır foo.
  • Daha sonra, değişkene atanan bir işaretçi vererek &uygulanır .foofoo

&Sadece, olmasa da (bir işlev işaretçisi dönüştürülmüş bir işleve bağlı uygulanabilir tabii ki, işlev işaretçisi sonucu işaretçi-a-pointer- bu durumda bir değişken olup, sürece örneğin, listenize ekleyebilirsiniz void (**pp_foo)() = &p7_foo;).

Bu yüzden &&fooçalışmıyor: &foobir işlev değil; bir değer olan bir işlev işaretleyicisidir. Bununla birlikte, &*&*&*&*&*&*fooolduğu gibi, çalışmak &******&fooiçin bu ifadelerin hem de, &her zaman bir işleve değil, bir rvalue işlev işaretçisi uygulanır.

Ayrıca *işlev işaretçisi aracılığıyla arama yapmak için tekli telefon kullanmanız gerekmediğini unutmayın ; hem (*p1_foo)();ve (p1_foo)();yine nedeniyle fonksiyon için fonksiyonlu işaretçisi dönüşüm, aynı sonucu vardır.


2
@Jimmy: Bunlar işlev işaretleyicilerine referans değil, sadece işlev işaretçileridir. &fooadresinin beklendiği gibi fooişaret ettiği bir işlev işaretçisi ile sonuçlanır foo.
Dennis Zickefoose

2
&İşleçleri nesneler için de zincirleyemezsiniz : verilen int p;, &pbir işaretçi verir pve bir değer ifadesidir; &Operatör, lvalue ifade gerektirir.
James McNellis

12
Katılmıyorum. Daha fazla *, daha az neşeli .
Seth Carnegie

28
Lütfen örneklerimin sözdizimini düzenlemeyin. Dilin özelliklerini göstermek için örnekleri çok özel olarak seçtim.
James McNellis

7
Bir yan not olarak, C standardı açıkça &*birbirinin iptal kombinasyonunun (6.5.3.2): "The unary & operator yields the address of its operand."/ - / olduğunu belirtir "If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.".
Lundin

9

Bence C'nin sadece altta yatan makine için bir soyutlama olduğunu ve bu soyutlamanın sızdığı yerlerden biri olduğunu hatırlamakta fayda var.

Bilgisayar açısından bakıldığında, bir işlev yalnızca yürütüldüğünde başka yönergeler gerçekleştiren bir bellek adresidir. Yani C'deki bir fonksiyonun kendisi bir adres olarak modellenir, bu da muhtemelen bir fonksiyonun işaret ettiği adresle "aynı" olduğu tasarımına yol açar.


0

&ve *bir sembol İdempotent işlemler C bir fonksiyonu olarak beyan edildiği anlamına gelir func == *func == &func == *&funcve bu nedenle,*func == **func

Bu tür anlamına gelir int ()aynıdır int (*)()bir fonksiyon parametresi gibi geçirilebilir tanımlanmış fonksiyon olarak *func, funcveya &func. (&func)()ile aynıdır func(). Godbolt bağlantısı.

Bir fonksiyon nedenle gerçekten bir adrestir *ve &anlamı yoktur ve bunun yerine bir hata üretme, derleyici seçer değer işlev adresi olarak yorumlamak.

&sembolü bir işlev işaretçisi olarak ilan üzerine, ancak (şimdi ayrı bir amacı vardır, çünkü) ibrenin adresi alır, oysa funcpve *funcpaynı olacaktır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.