Soru C cinsinden bir işlev adı nedir? [çift]


Bu sorunun zaten bir cevabı var:

Ben her zaman C’de anladım. func ve &func eşdeğerdi. İkisinin de Win64 sistemimde 8 byte olan tip pointer olması gerektiğini farz ediyorum. Ancak, sadece bunu denedim:

#include <stdio.h>

int func(int x, int y)
{
    printf("hello\n");
}

int main()
{
    printf("%d, %d\n", sizeof(&func), sizeof(func));
    return 0;
}

Ve çıktı almak için bekliyor 8, 8 almak için sürpriz oldu 8, 1 yerine.

Bu neden? Tam olarak ne tip olduğu  func? Tipik gibi görünüyor char veya eşdeğer.
Burada neler oluyor?

Bunu derledim gcc -std=c99 eğer bir fark yaratırsa.


32
2017-09-15 20:00


Menşei


func "işlev" türündendir ve gerçekten tanımlı bir boyuta sahip değildir. &func "bir işleve işaretçi" dir, bu yüzden boyutu bir işaretçinin boyutudur. - lurker
gcc -Wall -Wextra -pedantic -std=c99 senin arkadaşın - Mat
@Mat sen unuttun -Werror -pedantic-errors.
Neden düşüş var? - baruch
@mbratch: İşlev göstergelerinin, nesne işaretçileriyle aynı boyuta sahip olmadığına dikkat etmek önemlidir: c-faq.com/ptrs/generic.html - hugomg


Cevaplar:


C cinsinden bir işlev adı nedir?

Bir işlev adı veya fonksiyon göstergesi bir işlev türüne sahiptir. Bir ifadede kullanıldığında, işlenenin ne olduğu dışında sizeof veya & Operatör, bu türden döner "işlevi dönen tipdönen bir işleve "yazmak" işaretçisi ". (Bu C99, 6.3.2.1p4'de belirtilmiştir).

şimdi

sizeof(func)

geçerli C değil sizeof bir işlev tipi işlenene izin verilmez. Bu, kısıtlamalarda belirtilmiştir sizeof Şebeke:

(C99, 6.5.3.4p1 Kısıtlamaları) "Değer operatörü, işlev türüne veya tamamlanmamış bir türe, böyle bir türün parantezli adına veya bir bit alanı üyesi atanan bir ifadeye sahip bir ifadeye uygulanmamalıdır. "

Fakat

sizeof(func)

GNU C'de izin verilir

GNU C'de GNU C'ye ve buna izin veren bir GNU uzantısı vardır. sizeof bir işlev tipi verim işlenen ile 1:

6.23 Boşlukta Aritmetik ve İşlev İşaretleyicileri

[...] Ayrıca, geçersiz ve işlev türleri üzerinde izin verilir ve 1 döndürür.

http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html


36
2017-09-15 20:02



Bunu neden seçtiğine dair bir gerekçe var mı? ve 1 dönüş mü? Bana gereksiz bir karışıklık kaynağı yaratmış gibi geliyor. - Theodoros Chatzigiannakis
@TheodorosChatzigiannakis Çünkü GNU insanlarının kafası karışmış durumda. Sadece takip etmeyi önerdikleri C kodlama stiline bakın. Aaaaargh!
@TheodorosChatzigiannakis Bağlantıya bir göz atın, bu işaretçi aritmetiğine izin vermek için yapılır void * ve GNU C'de fonksiyonlara işaretçi. - ouah
@ouah: İşaretçi aritmetik ile void* Muhtemelen yararlı olabilir çünkü tüm oyuncular için char* sadece ham bellekle uğraşırken işaretçiyi ilerletmek için. Ancak işaretçi işlevleri ile aritmetik sadece kötülüktür. - rodrigo
@TheodorosChatzigiannakis Sadece spekülasyonu, kırılmamayı seçtikleri gcc geriye dönük uyumluluk olayı olabilir. Gcc, her şeyden önce C89'dan önce geliyor, bu yüzden bu davranış ilk gcc'deyse, herhangi bir standardı henüz kırmadı :-) - hyde


Verilen:

int func(int x, int y) { /* ... */ }

ifade func işlev tipindedir. Özellikle, türü int(int, int), C türü "iki işleviyle" sözdizimi int dönen parametreler int. (Bu tür sözdizimini sık sık görmezsiniz, çünkü doğrudan işlev türlerine başvurmak yaygın değildir.)

İçinde çoğu bağlamlarda, işlev türünün bir ifadesi dolaylı olarak işleve bir göstericiye dönüştürülür; bu durumda, işaretçi tipindedir int(*)(int, int).

Bu örtük dönüşümün yaptığı bağlamlar değil oluşur:

  1. İfade tekdüze operand olduğunda &; bu durumda, &func işlevin adresini verir (tıpkı func kendi kendine genellikle ) Yapar; ve

  2. İfade, işlenenin sizeof. Bu istisna olmaksızın, sizeof func Bir fonksiyon göstergesinin büyüklüğünü verir. Bunun yerine, bir Kısıtlama ihlaliderleyiciden bir tanılama gerektirir.

(Bir yan not: Bu dönüşüm, işlev adı bir işlev çağrısında kullanıldığında gerçekleşir. () "operatörü" (standart bunu belirtmez) işaretçi-işlev türünün bir önekini gerektirir.)

gcc standart dışı bir uzantıya sahip olur; işlev göstergelerinde ve türünde işaretçi aritmetiğine izin verir void*üzerinde işaretçi aritmetiği gibi davranıyor char* işaretçiler (yani, bayt birimleriyle çalışır). Ne yazık ki IMHO, gcc bunu fonksiyon kiplerinin büyüklüğünü ve tipini belirleyerek bir kludge ile gerçekleştirdi void 1. Bu yüzden sizeof func == 1; standart uygun modlardan birini etkinleştirirseniz (ör. gcc -std=c99 -pedantic), bir uyarı alacaksınız.

Bu arada, kullanma %d sonucunu yazdırmak için sizeof. sizeof türün sonucunu verir size_t. Uygulamanız destekliyorsa (C99 veya üstü), kullanın %zu; eğer değilse, açıkça dönüştürmek için bir cast kullanmanız gerekir. size_t yazdırabileceğiniz bir şeye değer. Örneğin:

printf("%lu\n", (unsigned long)sizeof &func);

12
2017-09-15 20:11



Nerede düşündüğün printf("%lu\n", (unsigned long) sizeof &func) son paragrafta? - chux
@chux: Evet, teşekkürler! - Keith Thompson
Bir fonksiyon gösterici ile işaretçi aritmetiğinin kullanımının ne anlama geldiğini merak ediyorum. Birlikte void * Anlayabiliyorum ama ne var (funcptr+1) demek istiyorsun? - glglgl
@glglgl: Bunun için hiçbir zaman kullanmadım, ancak bir fonksiyonun başlangıcından önce veya sonra (makine kodunun nasıl yerleştirildiğini biliyorsanız) N-bit adresinin hesaplanması için bazı düşük seviyeli kullanımlar vardır. İki işlev işaretçisi arasındaki farkı hesaplar. - Keith Thompson
@glglgl bence bu daha çok bir durum, bu gcc insanlar C'yi umuyor programcı ne yaptığını biliyor. Hangi, sanırım, biraz C'nin ruhunda - hyde


C cinsinden bir işlev adı nedir?

Bu bir işlev türü.

Ben her zaman C’de anladım. func ve &func eşdeğerdi

Eh, onlar "eşdeğer" değildir. Bununla birlikte, bir işlev çürütmek bir işaretçi-işlevi.

İkisinin de tipte işaretçi olması gerektiğini düşünüyorum.

Bu yanlış bir varsayım.

Ve 8, 8 çıktısını almayı beklerken 8, 1 yerine 1 elde etmek için şaşırdı. Bu neden?

Çünkü 1. eğer derlerse UB, 2. ilk etapta bile derleme yapmamalı ve bu nedenle programınız herhangi bir şey yapmakta özgürdür.


5
2017-09-15 20:05



@Mat Bir kısıtlama ihlali olduğundan, derlerse, UB'yi çağırır. Bir şey için derleme yapmamalı, ama burada görünüşe göre. O yapar değil demek istedim şart uzatma / belgelenmiş olmak.
@Mat Ve uygun olmayan bir uygulama tarafından üretilen bir program ... UB çağırır! Yoksa öyle değil mi? (Oh, ve kesinlikle konuşuyoruz, GCC, -Werror -Wall -Wextra -pedantic -pedantic-errors -ansibelki de.)
Tamam, gürültü için üzgünüm. Uygun bir şey düşünüyordum. Derleme olmamalı, durum böyle değil, teşhis etmeli. Uygun bir impl olursa ortaya çıkan ikili ile ne yapabileceğiniz hakkında hiçbir fikrim yok. size çöp olduğunu söyledikten sonra bir tane üretiyor :-) - Mat
@Mat Muhtemelen iki programcının davranışı hakkında tartışmak için kullanılabilir ;-) ;-)
@Bence bu durumda uyarı demek, "bu ikili, gcc'ye özgü iyi tanımlanmış bir davranışı vardır, ancak bunu derleyici ile uyumlu farklı standartlarla derlerseniz, o İkili, tüm dosyalarınızı silebilir ". Eski kodu, standartlarla uyumlu olacak şekilde düzeltiyorsanız yararlıdır. - hyde


Adlar C cinsinden bir türe sahip değildir. Bazı isimler typedef isimleri, nesneler veya işlevler gibi tür içeren varlıkları belirtir. Diğer tür isimler, önişlemci sembolleri gibi bir türü olmayan varlıkları belirtir. goto etiketler. Yine de başka türden isimler sadece türlerini, yani kendilerini gösterir. typedef isimler.

Bir işlevin adı, işlev türüne sahip bir varlığı gösterir. Bu fonksiyon tipi, geri dönüş tipi ve parametrelerle ilgili (muhtemelen tamamlanmamış) bilgileri içerir.

Fonksiyonlar değer olarak kullanıldığında, her zaman işaretçi-işlev türleri olarak manipüle edilir. Böyle bir işlev, taşınabilir bir C programında geçirilemez, ancak bir işlevin bir göstergesi olabilir.

Kavramsal olarak, doğrudan bir çağrıda bile foo(), ne oluyor bu foobir işlevin ifade edildiği bir ifade, değerlendirmenin dolaylı olarak bir işaretçi-fonksiyon değerine dönüştürüldüğü anlamına gelir. () işlev çağrısı postfix operatörü daha sonra bu işaretçi aracılığıyla işlevi çağırır.

İşlev türüne sahip bir ifadenin bir işaretçi değeri üreteceği bir kural vardır. dışında eğer bu ifade işlenenin & (adresi) veya sizeof Şebeke. func ve &func sadece aynı değeri üretmeleri anlamında eşdeğerdirler. func dolaylı olarak bir işaretçi değeri üretir. &func Bir işaretçinin örtük neslini bastırır (func işlenen & ve böylece dönüşüm bastırılır), ama sonra & adresi alır.

Yani bunu görebilirsiniz sizeof &func ve sizeof func farklıdır. Birincisi bir işaretçinin boyutunu alır ve ikincisi bir işlevin boyutunu almaya çalışır.

Bir fonksiyonun boyutunun alınması C'de bir kısıtlama ihlalidir: standarda uygun bir uygulamadan bir teşhis gerektirir. Program hala çevirirse ve bir değeri 1çalıştırıldığında üretilir, yani dil uygulamanıza özgü "bonus" davranışıdır. Standart dilde değildir.


1
2017-09-16 03:29