2010-08-26 11 views
25

To może być głupie pytanie, ale w jaki sposób operator sizeof zna wielkość argumentu tablicowego, gdy nie przekazujesz liczby elementów w tablicy. Wiem, że nie zwraca wszystkich elementów w tablicy, ale rozmiar w bajtach, ale aby uzyskać, że nadal musi wiedzieć, kiedy tablica się kończy. Ciekawe, jak to działa.Jak sizeof zna rozmiar tablicy operandów?

+0

Patrz: http://stackoverflow.com/questions/492384/how-to-find-the-sizeof-a-pointer-pointing-to-an-array –

+6

+1 do wszystkich tych, którzy powiedział ' sizeof' był operatorem czasu kompilacji, -1 wszystkim tym, którzy powiedzieli mu, że śledził rozmiar tablicy wskazywanej przez wskaźnik w czasie wykonywania. –

+0

Wow! dziesięć odpowiedzi, dopóki nie skończyłem odpowiedzi. Starzeję się ;-) – stacker

Odpowiedz

39

sizeof interpretowany jest w czasie kompilacji, a kompilator wie, w jaki sposób tablica została zadeklarowana (a tym samym ile zajmuje woluminu). Wywołanie sizeof na tablicie przydzielanej dynamicznie prawdopodobnie nie spowoduje tego, co chcemy, ponieważ (jak wspomniano), punkt końcowy tablicy nie jest określony.

+0

Jestem pewien, że nie jest to prawdą, zarówno w dyskusji nad samym pytaniem, jak i przy czytaniu specyfikacji - przynajmniej w przypadku współczesnej C. Lepiej byłoby, gdyby ta odpowiedź została usunięta. http: //en.wikipedia.org/wiki/Sizeof – xaxxon

+2

@xaxxon: Standard nie * wyraźnie * mówi, że jest oceniany podczas kompilacji, ale mówi, że 'sizeof' nie ocenia swojego operandu. Wyrażenie argumentu operandu ma znaczenie tylko dla kompilatora, więc jest to jedyne miejsce, w którym można je zaimplementować. Umowy VLA są obsługiwane pośrednio przez kompilator. Kompilator musi już wygenerować kod, aby obliczyć, ile miejsca należy przydzielić dla VLA (rozmiar członka tablicy * jakąś zmienną), aby mógł ponownie użyć tej liczby dla dowolnego 'sizeof' wywołania tej VLA. – bta

9

Kompilator zna rozmiar każdego typu w aplikacji, a sizeof po prostu prosi kompilator o podanie tej wartości.

+0

To nie jest prawda. wywołania sizeof tablicy zmiennej długości nie są obsługiwane w czasie kompilacji. Powinieneś zaktualizować swoją odpowiedź lub ją usunąć. http://pl.wikipedia.org/wiki/Sizeof – xaxxon

+0

@xaxxon: Obecnie tablice o zmiennej długości są nieważne w C++, więc odpowiedź brzmi: rozmiar * wszystkich poprawnych typów C++ * jest obliczany w czasie kompilacji. Tablice o zmiennej długości dla C++ zostały zaproponowane komitetowi podczas ostatniego spotkania, a propozycja została dobrze przyjęta, ale nie będzie to poprawne C++, dopóki nie zostanie zatwierdzony następny standard (C++ 14). Nawet w przypadku VLA kompilator * zna * rozmiar (zna wyrażenie, z którego została utworzona tablica, dzięki czemu może zapisać numer na boku), ponieważ VLA nie może scapeować zakresu funkcji, którą kompilator może wstrzyknąć w tę samą wartość, jest potrzebne. –

8

Sizeof jest operatorem czasu kompilacji; ma tyle informacji, ile robi kompilator. (I oczywiście kompilator zna zna rozmiar tablicy).

Dlatego jeśli wywołasz sizeof na wskaźniku, otrzymasz szerokość wskaźnika, a nie rozmiar tablicy, do której wskazuje ten wskaźnik.

+0

Tak więc (nie używając C++ przez kilka lat), co się stanie, jeśli zapytasz o sizeof i dereference wskaźnik do tablicy? Czy sizeof po prostu zawiedzie, czy szuka czegoś, co nagrało o rozmiarze tablicy i zwróciło to? –

+2

Jeśli usuniesz wskaźnik, otrzymasz odwołanie do obiektu, którego tablica będzie przechowywana, a sizeof zwróci to. 'int a [5]; int * p = a; assert (sizeof (* p) == sizeof (int); ' –

+0

To nie jest prawda, wywołania sizeof tablicy długości zmiennej nie są obsługiwane w czasie kompilacji.Musisz zaktualizować swoją odpowiedź lub ją usunąć http: //en.wikipedia. org/wiki/Sizeof – xaxxon

2

Jeśli używasz sizeof dla zmiennej lokalnej, wie, ile zadeklarowanych elementów. Jeśli używasz sizeof na parametrze funkcji, nie wie; traktuje parametr jako wskaźnik do tablicy, a sizeof podaje rozmiar wskaźnika.

+0

To robi wiedzieć. Parametr * jest * wskaźnikiem (do pierwszego elementu, a nie do tablicy), a kompilator zna rozmiar wskaźnika. C nie ma parametrów tablicy. –

12

Z wyjątkiem jednego przypadku, sizeof robi to podczas kompilacji. W czasie kompilacji kompilator śledzi typ obiektu pełny [Edytuj: dobrze, wszystko, co wie o typie obiektu, w każdym razie - jeśli typ nie jest kompletny, więc nie zawiera rozmiaru , próba użycia sizeof zakończy się niepowodzeniem], a sizeof zasadniczo "eksportuje" jedną część tej informacji z kompilatora do kompilowanego kodu, więc staje się w zasadzie stałą w wynikowym kodzie.

Wyjątkiem jest zastosowanie sizeof do macierzy o zmiennej długości (VLA) . Po zastosowaniu do VLA, sizeof oblicza swój operand (który nie jest inaczej) i tworzy rzeczywisty rozmiar VLA. W tym przypadku wynik nie jest stały.


1. Włas oficjalnie stał się częścią C w C99, ale niektóre kompilatory obsługiwane je wcześniej. Chociaż nie są oficjalnie częścią C++, niektóre kompilatory (np. G ++) zawierają VLA jako rozszerzenie C++.

+3

Kompilator śledzi pełny typ ... chyba że nie, ponieważ typ jest niekompletny. W takim przypadku nie można użyć 'sizeof'. – Potatoswatter

+0

@Potatoswatter: dobry punkt. Zmodyfikowano, aby odzwierciedlić ten fakt. –

+0

To nie jest prawda. wywołania sizeof tablicy zmiennej długości nie są obsługiwane w czasie kompilacji. Powinieneś zaktualizować swoją odpowiedź lub ją usunąć. http://en.wikipedia.org/wiki/Sizeof – xaxxon

1

Cytat wiki:

Jest obowiązkiem autora kompilatora wdrożyć operatora sizeof w sposób specyficzny i prawidłowy dla danej realizacji języka. Operator wielkości musi uwzględniać implementację podstawowego schematu alokacji pamięci , aby uzyskać rozmiary różnych typów danych .sizeof to zazwyczaj operator czasu kompilacji, który oznacza, że ​​podczas kompilacji rozmiar i jego operand zostają zastąpione przez wartość wyniku . Jest to widoczne w kodzie języka asemblerowym wytwarzanego przez kompilator C lub C++. Z tego powodu, sizeof kwalifikuje się jako operator, nawet , chociaż jego użycie czasami wygląda na wywołanie funkcji .

+2

To prawda, ale tak naprawdę nie odpowiada na pytanie OP. –

1

sizeof operator „wie” rozmiar wszystkich typów danych atomowych, ponieważ kodowanym, związki i tablice mogą być budowane tylko przez łączenie typów atomowych jest to łatwe do określenia rozmiaru tablicy dowolnego typu. Używa podstawowej arytmetyki do określania typów złożonych (podczas kompilacji).

4

Rozmiar może być stosowany tylko do całkowicie zdefiniowanych typów. Kompilator albo będzie w stanie określić rozmiar w czasie kompilacji (np. Jeśli masz deklarację typu int foo [8];), albo będzie mógł określić, że musi dodać kod do śledzenia wielkości zmiennej -elength array (np. jeśli masz deklarację typu int foo [n + 3];).

W przeciwieństwie do innych odpowiedzi tutaj, należy pamiętać, że od C99 sizeof() jest nie koniecznie określone podczas kompilacji, ponieważ tablice mogą mieć zmienną długość.

+0

s/runtime/compile czas/g i staje się dobrą odpowiedzią. – ninjalj

+0

Derp. Dzięki. :-) – ngroot

+0

Rozmowa o samospełniających się przepowiedniach :) – ninjalj

19

Problem, który leży u podstaw twojego problemu, aby to zrozumieć, może być spowodowany mylącymi tablicami i wskaźnikami, tak jak czyni to wiele osób. Jednak tablice nie są wskaźnikami. A double da[10] to tablica dziesięciu double, a nie double*, i na pewno jest znana kompilatorowi, gdy zapyta się go o ocenę sizeof(da). Nie zdziwiłbyś się, że kompilator zna sizeof(double)?

Problem z tablicami polega na tym, że automatycznie rozpraszają się do wskaźników do pierwszych elementów w wielu kontekstach (np. Kiedy są przekazywane do funkcji). Ale nadal tablica to tablice, a wskaźniki to wskaźniki.

+2

+1 za wyjaśnienie przyczyny tego nieporozumienia. –

+0

Absolutnie w porządku, sbi. Rozdziały 4, 9 i 10 znakomitej książki Petera van der Lindena "Expert C Programming" świetnie sobie radzą z rozróżnianiem wskaźników i tablic. Piekło, rozdział 4 zatytułowany jest "Szokująca prawda: tablice i wskaźniki C NIE są takie same", tak jak powiedział sbi. I poświęca temu 15 stron. Sprawdź spis treści, zwł. Ch. 4, 9 i 10: http://books.google.com/books?id=9t5uxP9xHpwC&lpg=PP1&dq=expert%20c%20programming&pg=PR8#v=onepage&q&f=false – Dan

0

sizeof jest obliczany podczas kompilacji. Dlatego przy tworzeniu tablicy dynamicznej tworzy się ją w następujący sposób.

char * array; 
int size; 
//Get size somehow. 
array = malloc(size*(sizeof(char))); 

// teraz podczas kompilacji kompilator zna na pewno rozmiar znaku. ponieważ musi wyrównać je w pamięci. W tym momencie system operacyjny wie, jaki rozmiar musi przydzielić.

Z drugiej strony tablice o zmiennej długości są tworzone na stosie Stack. Ale każda przydzielona pamięć typu malloc zostanie utworzona na stercie.

0

Rozmiar jest zawsze oceniany w czasie kompilacji. W kompilatorze wieloprzebiegowym podczas generowania tabeli symboli kompilator musi określić rozmiar każdego symbolu zadeklarowanego do dalszego działania w celu wygenerowania kodu pośredniego. Tak więc dla wszystkich wielkości referowań w kodzie zastępuje dokładną wartość. Na etapie generowania kodu pośredniego wszystkie operatory, wyciągi, są konwertowane na odpowiedni pośredni kod (ASM/inny format). Wreszcie etap generowania kodu m/c konwertuje go na kod maszynowy.

Niektóre dyskusje widoczne powyżej w.r.t dynamiczne alokacje dotyczące sizeof nie są w ogóle w kontekście. Dowolne odniesienie do rozmiaru (* p), gdzie p jest wskaźnikiem dowolnego typu danych, kompilator po prostu znajduje typ danych * p i zastępuje jego rozmiar, a nie idzie sprawdzić nagłówek MCB przydzielonego bloku, aby zobaczyć, co jest przydzielone rozmiar pamięci. To nie jest w czasie wykonywania.Na przykład podwójne * p; sizeof (* p) wciąż można wykonać bez przydzielania pamięci dla wskaźnika p. Jak to jest możliwe?

+0

"Rozmiar jest zawsze oceniany w czasie kompilacji." Nie dotyczy to tablic o zmiennej długości, co omówiono w innych odpowiedziach i komentarzach do pytań. Ta odpowiedź powinna zostać usunięta i jest błędna dla bieżącego języka C (przeczytaj specyfikację). http://en.wikipedia.org/wiki/Sizeof – xaxxon

1

sizeof to zwykle oceniany podczas kompilacji. Godnym uwagi wyjątkiem są tablice zmiennych długości C99.

int main(int argc, char **argv) 
{ 
    if (argc > 1) 
    { 
     int count = atoi(argv[1]); 
     int someArray[count]; 

     printf("The size is %zu bytes\n", sizeof someArray); 
    } 
    else puts("No"); 
}