Dlaczego rozmiar funkcji w C zawsze wynosi 1 bajt?

87

Kiedy sprawdzamy rozmiar funkcji za pomocą sizeof(), zawsze otrzymujemy 1 bajt . Co oznacza ten 1 bajt?

user1645461
źródło

Odpowiedzi:

80

Jest to naruszenie ograniczenia i Twój kompilator powinien to zdiagnozować. Jeśli mimo to skompiluje, Twój program zachowuje się niezdefiniowane [dzięki @Steve Jessop za wyjaśnienie trybu awarii i zobacz odpowiedź @Michaela Burra, wyjaśniającą, dlaczego niektóre kompilatory na to zezwalają]: From C11, 6.5.3.4./ 1:

sizeofOperator nie stosuje się do wyrażenia, które ma typ funkcji

Kerrek SB
źródło
11
To jest ograniczenie, co oznacza, że ​​w zgodnym kompilatorze jest diagnozowane. Jeśli kompilator i tak go skompiluje (po zdiagnozowaniu), zachowanie jest niezdefiniowane. Jeśli kompilator nie zdiagnozuje tego (czego nie robi na przykład gcc -pedantic), masz niezgodny kompilator i każdy program ma niezdefiniowane zachowanie.
Steve Jessop
1
To zachowanie stawia go w tej samej kategorii, co rozszerzenie GNU C, ale nie mam pojęcia, dlaczego ktokolwiek chce tego zachowania, więc nie wiem, dlaczego autorzy GNU zrobili wszystko, co w ich mocy, aby je dodać.
Steve Jessop
1
@SteveJessop: Zauważ, że to jest nawet z -std=c11, nie gnu11 . To naprawdę dziwne rozszerzenie kompilatora.
Kerrek SB
6
Ooh! Założę się, że chodzi o włączenie arytmetyki na wskaźnikach funkcji, tak samo jak sizeof(void)1 w GNU C.
Steve Jessop
3
Odnośnie -std=c11: ktoś powinien odnieść się do -std=c*opcji Standardy reklamowe. Nie włączają trybu zgodności, a jedynie wyłączają rozszerzenia, które uniemożliwiałyby kompilację dobrze sformułowanego programu (na przykład jako typeofsłowo kluczowe, ponieważ dobrze sformułowany program w C może używać go jako nazwy zmiennej, ale gccdomyślnie odrzuciłby to ). Aby dodatkowo wyłączyć rozszerzenia, które pozwalają źle sformułowanym programom przejść niezdiagnozowane, potrzebujesz -pedanticlub -pedantic-errors.
Steve Jessop
56

To nie jest niezdefiniowane zachowanie - standard języka C wymaga diagnostyki podczas używania sizeofoperatora z desygnatorem funkcji (nazwą funkcji), ponieważ jest to naruszenie ograniczenia dla sizeofoperatora.

Jednak jako rozszerzenie języka C, GCC umożliwia arytmetykę na voidwskaźnikach i wskaźnikach funkcji, co jest wykonywane przez traktowanie rozmiaru a voidlub funkcji jako 1. W konsekwencji sizeofoperator oceni 1for voidlub funkcję z GCC. Zobacz http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html#Pointer-Arith

Możesz zmusić GCC do wydawania ostrzeżeń podczas używania sizeofz tymi operandami, używając opcji -pedanticlub -Wpointer-arithw GCC. Lub zrób z tego błąd -Werror=pointer-arith.

Michael Burr
źródło
Twoja logika jest błędna. C wymaga komunikatów diagnostycznych dla niektórych, ale nie dla wszystkich UB. Nie możesz stwierdzić, że coś ma określone zachowanie tylko dlatego, że istnieje diagnostyka.
MSalters
4
C wymaga diagnostyki dla wszystkich naruszeń ograniczeń (5.1.1.3 Diagnostyka w C99 lub C11). Ograniczenie (3,8 w C99 / C11) to „ograniczenie, syntaktyczne lub semantyczne, za pomocą którego należy interpretować ekspozycję elementów języka”, co wydaje się mówić, że coś, co nie jest zgodne z ograniczeniami, nie może być zinterpretowane .
Michael Burr
3
I żeby było jasne - naruszenie ograniczenia nie skutkuje niezdefiniowanym zachowaniem. To błąd, jak błąd składniowy. Na przykład norma mówi, że „jeśli naruszony jest wymóg„ powinien ”lub„ nie powinien ”, który pojawia się poza ograniczeniem , zachowanie jest nieokreślone”. Jeśli naruszenie ograniczenia skutkowałoby UB, dlaczego standard mówiłby tylko o „obowiązkach” i „zakazach”, których nie ma w tym miejscu?
Michael Burr
3
Naprawdę nie powiedziałem nic o UB poza tym, że sizeoffunkcja nie jest UB (o czym wspomniałem tylko dlatego, że inne odpowiedzi mówiły, że to UB). Ale może pogmatwałem to ze względu na sposób, w jaki skonstruowałem zdanie. Żeby było jaśniej. sizeoffunkcja nie jest UB (jak twierdziło kilka odpowiedzi). Jest to naruszenie ograniczeń. Jako taki wymaga diagnostyki. GCC dopuszcza to jako rozszerzenie.
Michael Burr
2
@KerrekSB: OP nie otrzymuje diagnostyki, ponieważ prawdopodobnie używają GCC, co pozwala na to użycie jako rozszerzenia języka C.
Michael Burr
13

Oznacza to, że twórca kompilatora zdecydował się na wartość 1, zamiast sprawiać, że demony latają z twojego nosa (rzeczywiście, było to kolejne nieokreślone użycie, sizeofktóre dało nam to wyrażenie: "kompilator C sam MUSI wydać diagnostykę, JEŚLI jest to pierwsza wymagana diagnostyka wynikająca z twojego programu, a następnie MOŻE sama spowodować wylatywanie demonów z twojego nosa (co, nawiasem mówiąc, mogłoby być udokumentowanym komunikatem diagnostycznym) tak samo, jak MOŻE wydać dalszą diagnostykę pod kątem dalszych naruszeń reguł składni lub ograniczeń (lub, w tej sprawie, z dowolnego wybranego przez siebie powodu). ” https://groups.google.com/forum/?fromgroups=#!msg/comp.std.c/ycpVKxTZkgw/S2hHdTbv4d8J

Stąd slangowe określenie "demony nosowe" na wszystko, co kompilator zdecyduje się zrobić w odpowiedzi na niezdefiniowaną konstrukcję. 1jest nosowym demonem tego kompilatora w tym przypadku.

Jon Hanna
źródło
@IlmariKaronen Muszę jednak przyznać, że jest powód, dla którego większość moich odpowiedzi w C (i C ++) opiera się albo na ogólnych zasadach niezależnych od języka, albo na takich historycznych samorodkach. Moje doświadczenie z C graniczy z historią samą w sobie :)
Jon Hanna
7

Jak wskazywali inni, sizeof () może przyjąć dowolny prawidłowy identyfikator, ale nie zwróci prawidłowego (prawdziwie prawdziwego i prawidłowego) wyniku dla nazw funkcji. Co więcej, z całą pewnością może, ale nie musi, skutkować syndromem „wyskakującego z nosa demonów”.

Jeśli chcesz sprofilować rozmiar funkcji programu, sprawdź mapę linkera, którą można znaleźć w katalogu wyników pośrednich (tym, w którym rzeczy są kompilowane do .obj / .o lub gdzie znajduje się wynikowy obraz / plik wykonywalny). Czasami istnieje opcja, aby wygenerować ten plik mapy lub nie ... zależy to od kompilatora / konsolidatora.

Jeśli chcesz mieć rozmiar wskaźnika do funkcji, wszystkie mają ten sam rozmiar, rozmiar słowa adresującego na twoim procesorze.

jpinto3912
źródło
1
Jakiego rodzaju jest stwierdzenie „zdecydowanie może lub nie” ??? Czy nie dotyczy to dosłownie wszystkiego?
Kerrek SB
@KerrekSB tak, ale tutaj może, ale nie musi, robić cokolwiek i nadal być w ramach zasad. To prawda, że ​​kompilator może odmówić kompilacji lub nie, int x = 1;ale tylko jeden z nich jest dozwolony dla kompilatora zgodnego ze standardami. Po sizeof()zastosowaniu do funkcji może, ale nie musi, zwrócić ustawioną wartość, odmówić kompilacji lub zwrócić losową wartość na podstawie tego, co w danym momencie znajduje się w określonym rejestrze. Dosłowne demony nosowe są mało prawdopodobne, ale mieszczą się w literach normy.
Jon Hanna
@kerrek To może być prawdziwe dla niczego lub może nie być fałszywe w przypadku niczego ... spójrz na to jako niewyraźne i nielogiczne.
jpinto3912
1
Jeśli chcesz poznać rozmiar wskaźnika do funkcji, zastosuj sizeofwskaźnik do funkcji.
Alexis