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?

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


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.


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.


@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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.