Wygenerowany kod będzie identyczny, ponieważ kompilator zna typ * int_arr w czasie kompilacji (a zatem wartość sizeof (* int_arr)). Będzie stała, a kompilator może odpowiednio zoptymalizować.
Mark Harrison
10
Tak powinno być w przypadku wszystkich kompilatorów, ponieważ wyniki sizeof są zdefiniowane jako stała czasowa kompilacji.
Mark Harrison
449
Ważne : nie przestawaj czytać tutaj, przeczytaj następną odpowiedź! Działa to tylko dla tablic na stosie , np. Jeśli używasz malloc () lub uzyskujesz dostęp do parametru funkcji, nie masz szczęścia. Patrz poniżej.
Markus
7
Do programowania interfejsu API systemu Windows w C lub C ++ istnieje ARRAYSIZEmakro zdefiniowane w WinNT.h(które jest pobierane przez inne nagłówki). Dlatego użytkownicy WinAPI nie muszą definiować własnego makra.
Lumi,
17
@ Markus działa dla każdej zmiennej, która ma typ tablicy; to nie musi być „na stosie”. Np static int a[20];. Ale twój komentarz jest przydatny dla czytelników, którzy mogą nie zdawać sobie sprawy z różnicy między tablicą a wskaźnikiem.
MM
807
sizeofDroga jest właściwa droga IFF masz do czynienia z tablicami nie odebranych jako parametry. Tablica wysłana jako parametr do funkcji jest traktowana jako wskaźnik, więc sizeofzwróci rozmiar wskaźnika, zamiast rozmiaru tablicy.
Dlatego w funkcjach wewnętrznych ta metoda nie działa. Zamiast tego zawsze przekazuj dodatkowy parametr size_t sizewskazujący liczbę elementów w tablicy.
Test:
#include<stdio.h>#include<stdlib.h>void printSizeOf(int intArray[]);void printLength(int intArray[]);int main(int argc,char* argv[]){intarray[]={0,1,2,3,4,5,6};
printf("sizeof of array: %d\n",(int)sizeof(array));
printSizeOf(array);
printf("Length of array: %d\n",(int)(sizeof(array)/sizeof(array[0])));
printLength(array);}void printSizeOf(int intArray[]){
printf("sizeof of parameter: %d\n",(int)sizeof(intArray));}void printLength(int intArray[]){
printf("Length of parameter: %d\n",(int)(sizeof(intArray)/sizeof(intArray[0])));}
Dane wyjściowe (w 64-bitowym systemie operacyjnym Linux):
sizeof of array:28sizeof of parameter:8Length of array:7Length of parameter:2
Dane wyjściowe (w 32-bitowym systemie operacyjnym Windows):
sizeof of array:28sizeof of parameter:4Length of array:7Length of parameter:1
dlaczego jest tak, length of parameter:2jeśli przekazywany jest tylko wskaźnik do pierwszego elementu tablicy?
Bbvarghe,
16
@Bbvarghe To dlatego, że wskaźniki w systemach 64-bitowych mają 8 bajtów (sizeof (intArray)), ale ints wciąż (zwykle) mają 4 bajty (sizeof (intArray [0])).
Elideb
13
@Pacerier: Nie ma poprawnego kodu - zwykle rozwiązaniem jest podanie długości wraz z tablicą jako osobnego argumentu.
Jean Hominal
10
Zaczekaj, więc nie ma sposobu, aby uzyskać dostęp do tablicy bezpośrednio ze wskaźnika i zobaczyć jej rozmiar? Nowy w C tutaj.
sudo
7
@Michael Trouw: można użyć składni operatora, jeśli to sprawia, że czujesz lepiej: (sizeof array / sizeof *array).
chqrlie
134
Warto zauważyć, że sizeofnie pomaga to w przypadku wartości tablicy, która uległa rozkładowi na wskaźnik: mimo że wskazuje na początek tablicy, w przypadku kompilatora jest ona taka sama jak wskaźnik do pojedynczego elementu tej tablicy . Wskaźnik nie „pamięta” niczego innego o tablicy, która została użyta do jego zainicjowania.
int a[10];int* p = a;
assert(sizeof(a)/sizeof(a[0])==10);
assert(sizeof(p)==sizeof(int*));
assert(sizeof(*p)==sizeof(int));
@ Magnus: Standard definiuje rozmiarof jako uzyskiwanie liczby bajtów w obiekcie, a rozmiarof (char) jest zawsze jeden. Liczba bitów w bajcie zależy od implementacji. Edycja: ANSI C ++ sekcja standardowa 5.3.3 Sizeof: "Operator sizeof zwraca liczbę bajtów w reprezentacji obiektowej swojego argumentu. [...] sizeof (char), sizeof (char podpisany) i sizeof (char podpisany) to 1; wynik sizeof zastosowany do dowolnego innego podstawowego typu jest zdefiniowany w implementacji. ”
Skizz
Sekcja 1.6 Model pamięci C ++: „Podstawową jednostką pamięci w modelu pamięci C ++ jest bajt. Bajt jest wystarczająco duży, aby pomieścić dowolny element podstawowego zestawu znaków wykonawczych i składa się z ciągłej sekwencji bitów, liczby z których jest zdefiniowany implementacja. ”
Skizz
2
Pamiętam, że CRAY miał C z char32 bitami. Wszystko, co mówi standard, to to, że mogą być reprezentowane wartości całkowite od 0 do 127, a jego zakres wynosi co najmniej -127 do 127 (znak jest podpisany) lub 0 do 255 (znak jest niepodpisany).
vonbrand
5
To doskonała odpowiedź. Chcę skomentować, że wszystkie powyższe twierdzenia są ocenione na PRAWDA.
Javad
49
Rozmiar „sztuczki” to najlepszy sposób, jaki znam, z jedną małą, ale (dla mnie, to jest główną wkurzaniem) istotną zmianą w stosowaniu nawiasów.
Jak wyjaśnia wpis w Wikipedii, C sizeofnie jest funkcją; to operator . Dlatego nie wymaga nawiasów wokół argumentu, chyba że argument jest nazwą typu. Łatwo to zapamiętać, ponieważ sprawia, że argument wygląda jak rzutowane wyrażenie, które również używa nawiasów.
Więc: jeśli masz następujące elementy:
int myArray[10];
Możesz znaleźć liczbę elementów o takim kodzie:
size_t n =sizeof myArray /sizeof*myArray;
To dla mnie brzmi o wiele łatwiej niż alternatywa z nawiasami. Popieram także użycie gwiazdki w prawej części działu, ponieważ jest bardziej zwięzła niż indeksowanie.
Oczywiście, to także czas kompilacji, więc nie musisz się martwić podziałem wpływającym na wydajność programu. Więc skorzystaj z tego formularza, gdziekolwiek możesz.
Zawsze najlepiej jest używać sizeof na rzeczywistym obiekcie, gdy go masz, a nie na typie, ponieważ wtedy nie musisz się martwić o popełnienie błędu i podanie niewłaściwego typu.
Załóżmy na przykład, że masz funkcję, która wyprowadza niektóre dane jako strumień bajtów, na przykład przez sieć. Wywołajmy funkcję send()i sprawmy, aby jako argumenty przyjmowała wskaźnik do obiektu do wysłania oraz liczbę bajtów w obiekcie. Prototyp staje się:
void send(constvoid*object,size_t size);
A potem musisz wysłać liczbę całkowitą, więc kodujesz to w ten sposób:
int foo =4711;
send(&foo,sizeof(int));
Teraz wprowadziłeś subtelny sposób strzelania sobie w stopę, określając typ foow dwóch miejscach. Jeśli jeden się zmieni, a drugi nie, kod się zepsuje. Dlatego zawsze rób tak:
send(&foo,sizeof foo);
Teraz jesteś chroniony. Jasne, powielasz nazwę zmiennej, ale ma duże prawdopodobieństwo uszkodzenia w sposób, który kompilator może wykryć, jeśli ją zmienisz.
Przy okazji, czy są to identyczne instrukcje na poziomie procesora? Czy sizeof(int)wymaga mniej instrukcji niż sizeof(foo)?
Pacerier
@Pacerier: nie, są identyczne. Pomyśl o int x = 1+1;kontra int x = (1+1);. Tutaj nawiasy są absolutnie wyłącznie estetyczne.
quetzalcoatl
@Aidiakapi To nieprawda, weź pod uwagę C99 VLA.
zrelaksuj się
@ unwind Dzięki, poprawiam się. Aby poprawić mój komentarz, sizeofzawsze będzie stały w C ++ i C89. W przypadku tablic o zmiennej długości C99 można go oceniać w czasie wykonywania.
Aidiakapi
2
sizeofmoże być operatorem, ale zgodnie z Linusem Torvaldsem należy go traktować jako funkcję. Zgadzam się. Przeczytaj jego uzasadnienie tutaj: lkml.org/lkml/2012/7/11/103
Mały nitpick: wynik odejmowania wskaźnika ma typ ptrdiff_t. (Zwykle w systemie 64-bitowym będzie to większy typ int). Nawet jeśli zmienisz intna ptrdiff_tw tym kodzie, nadal zawiera błąd, jeśli arrzajmuje więcej niż połowę przestrzeni adresowej.
MM
2
@MM Kolejny mały nitpick: W zależności od architektury systemu przestrzeń adresowa nie jest tak duża, jak rozmiar wskaźnika w większości systemów. Na przykład Windows ogranicza przestrzeń adresową dla aplikacji 64-bitowych do 8 TB lub 44 bitów. Więc nawet jeśli masz tablicę większą niż połowa przestrzeni adresowej 4.1 TB, na przykład, nie będzie to błąd. Tylko jeśli Twoja przestrzeń adresowa przekracza 63 bity w tych systemach, można nawet napotkać taki błąd. Ogólnie nie martw się o to.
Aidiakapi
1
@Aidiakapi w 32-bitowym systemie Linux x86 lub Windows z /3Gopcją masz podział użytkownika / jądra 3G / 1G, co pozwala mieć rozmiar tablic do 75% wielkości przestrzeni adresowej.
Ruslan
1
Uważaj foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];za zmienne globalne. buf3[]deklaracja kończy się niepowodzeniem, ponieważ (&buf1)[1] - buf1nie jest stała.
chux - Przywróć Monikę
2
Jest to technicznie niezdefiniowane zachowanie, ponieważ standard wyraźnie nie zezwala na dereferencje poza końcem tablicy (nawet jeśli nie spróbujesz odczytać zapisanej wartości)
MM
26
Możesz użyć operatora sizeof, ale nie będzie on działał dla funkcji, ponieważ weźmie odwołanie do wskaźnika, możesz wykonać następujące czynności, aby znaleźć długość tablicy:
Jeśli znasz typ danych tablicy, możesz użyć czegoś takiego:
int arr[]={23,12,423,43,21,43,65,76,22};int noofele =sizeof(arr)/sizeof(int);
Lub jeśli nie znasz typu danych tablicy, możesz użyć czegoś takiego:
noofele =sizeof(arr)/sizeof(arr[0]);
Uwaga: Ta funkcja działa tylko wtedy, gdy tablica nie jest zdefiniowana w czasie wykonywania (np. Malloc), a tablica nie jest przekazywana w funkcji. W obu przypadkach arr(nazwa tablicy) jest wskaźnikiem.
int noofele = sizeof(arr)/sizeof(int);jest tylko w połowie lepszy niż kodowanie int noofele = 9;. Użycie sizeof(arr)zachowuje elastyczność w przypadku zmiany rozmiaru tablicy. Jednak sizeof(int)wymaga aktualizacji, jeśli rodzaj arr[]zmiany. Lepszy w użyciu, sizeof(arr)/sizeof(arr[0])nawet jeśli typ jest dobrze znany. Niejasne, dlaczego intdla for noofelevs. size_t, typ zwracany przez sizeof().
chux - Przywróć Monikę
19
Makro, z ARRAYELEMENTCOUNT(x)którego wszyscy korzystają, ocenia niepoprawnie . W rzeczywistości jest to tylko kwestia drażliwa, ponieważ nie można mieć wyrażeń, które skutkowałyby typem „tablicy”.
To naprawdę nie ma wiele wspólnego z rozmiarem tablic. Właśnie zauważyłem wiele błędów wynikających z nieobserwowania, jak działa preprocesor C. Zawsze zawijasz parametr makra, w którym nie może być zaangażowane żadne wyrażenie.
To jest poprawne; mój przykład był zły. Ale tak właśnie powinno być. Jak już wspomniałem p + 1, skończy jako typ wskaźnika i unieważni całe makro (podobnie jak w przypadku próby użycia makra w funkcji z parametrem wskaźnika).
Na koniec dnia, w tym konkretnym przypadku, błąd tak naprawdę nie ma znaczenia (więc po prostu marnuję czas wszystkich; huzzah!), Ponieważ nie masz wyrażeń z rodzajem „tablicy”. Ale tak naprawdę kwestia subtelności oceny preprocesora uważam za ważną.
Dziękuję za wyjaśnienie. Oryginalna wersja powoduje błąd czasu kompilacji. Clang informuje, że „wartość w indeksie nie jest tablicą, wskaźnikiem ani wektorem”. W tym przypadku wydaje się to preferowane zachowanie, chociaż twoje komentarze na temat kolejności oceny w makrach są dobrze przyjęte.
Mark Harrison
1
Nie myślałem o reklamacji kompilatora jako automatycznym powiadomieniu niepoprawnego typu. Dziękuję Ci!
3
Czy istnieje powód, aby nie używać (sizeof (x) / sizeof (*x))?
poważnedev
16
W przypadku tablic wielowymiarowych jest to nieco bardziej skomplikowane. Często ludzie definiują wyraźne stałe makr, tj
Zależnie od typu arrayma, nie musisz używać, sizeof(array) / sizeof(array[0])jeśli arrayjest tablicą albo char, unsigned charalbo signed char- Cytat z C18,6.5.3.4 / 4: „Gdy sizeof jest stosowane do operandu, który ma typ char, unsigned char lub podpisany char , (lub jego kwalifikowana wersja) wynik to 1. ” W takim przypadku możesz po prostu zrobić, sizeof(array)jak wyjaśniono w mojej dedykowanej odpowiedzi .
RobertS obsługuje Monikę Cellio
15
Rozmiar tablicy w C:
int a[10];size_t size_of_array =sizeof(a);// Size of array aint n =sizeof(a)/sizeof(a[0]);// Number of elements in array asize_t size_of_element =sizeof(a[0]);// Size of each element in array a // Size of each element = size of type
Ciekawy kod, który używany size_t size_of_elementjeszcze intz int n = sizeof (a) / sizeof (a[0]); niesize_t n = sizeof (a) / sizeof (a[0]);
chux - przywrócenie Monica
1
Cześć @Yogeesh HT, czy możesz odpowiedzieć na wątpliwości dotyczące chuxa? Jestem również bardzo ciekawy, jak int n = sizeof (a) / sizeof (a [0]) podaje długość tablicy i dlaczego nie używamy size_t dla długości tablicy. Czy ktoś może na to odpowiedzieć?
Brain
1
@Brain sizeof (a) daje rozmiar wszystkich elementów obecnych w tablicy a sizeof (a [0]) daje rozmiar 1 elementów. Załóżmy, że a = {1,2,3,4,5}; sizeof (a) = 20 bajtów (jeśli sizeof (int) = 4 bajty mnożą 5), sizeof (a [0]) = 4 bajty, więc 20/4 = 5 tzn. brak elementów
Yogeesh HT
2
@YogeeshHT W przypadku bardzo dużych tablic char a[INT_MAX + 1u];, int njak użyte w int n = sizeof (a) / sizeof (a[0]);jest niewystarczające (jest to UB). Korzystanie size_t n = sizeof (a) / sizeof (a[0]);nie powoduje tego problemu.
chux - Przywróć Monikę
13
Radziłbym nigdy nie używać sizeof(nawet jeśli można go użyć), aby uzyskać dowolny z dwóch różnych rozmiarów tablicy, albo w liczbie elementów, albo w bajtach, które są dwoma ostatnimi przypadkami, które tutaj pokazuję. Dla każdego z dwóch rozmiarów można użyć makr pokazanych poniżej, aby zwiększyć bezpieczeństwo. Powodem jest ujawnienie opiekunom intencji kodu i odróżnienie go sizeof(ptr)od sizeof(arr)pierwszego spojrzenia (które napisane w ten sposób nie jest oczywiste), aby błędy były widoczne dla wszystkich czytających kod.
Nie zgadzam się z rozwiązaniem, które zapewnia Linus, którym jest nigdy nie używać notacji tablicowej do parametrów funkcji.
Lubię notację tablicową jako dokumentację, że wskaźnik jest używany jako tablica. Oznacza to jednak, że należy zastosować rozwiązanie niezawodne, aby nie można było napisać błędnego kodu.
Z tablicy mamy trzy rozmiary, które chcielibyśmy poznać:
Rozmiar elementów tablicy
Liczba elementów w tablicy
Rozmiar w bajtach wykorzystywany przez tablicę w pamięci
Rozmiar elementów tablicy
Pierwszy jest bardzo prosty i nie ma znaczenia, czy mamy do czynienia z tablicą czy wskaźnikiem, ponieważ robi się to w ten sam sposób.
qsort() potrzebuje tej wartości jako trzeciego argumentu.
W przypadku pozostałych dwóch rozmiarów, które są tematem pytania, chcemy się upewnić, że mamy do czynienia z tablicą, a jeśli nie, zepsujemy kompilację, ponieważ jeśli mamy do czynienia ze wskaźnikiem, otrzymamy nieprawidłowe wartości . Gdy kompilacja zostanie zerwana, będziemy mogli łatwo zobaczyć, że nie mamy do czynienia z tablicą, ale ze wskaźnikiem, i będziemy musieli po prostu napisać kod ze zmienną lub makrem, który przechowuje rozmiar tablica za wskaźnikiem.
Liczba elementów w tablicy
Ten jest najczęstszy i wiele odpowiedzi dostarczyło typowego makra ARRAY_SIZE:
Biorąc pod uwagę, że wynik ARRAY_SIZE jest powszechnie używany z podpisanymi zmiennymi typu ptrdiff_t, dobrze jest zdefiniować podpisany wariant tego makra:
Tablice z więcej niż PTRDIFF_MAXczłonkami podadzą niepoprawne wartości dla tej podpisanej wersji makra, ale od odczytu C17 :: 6.5.6.9, takie tablice już się bawią ogniem. Tylko ARRAY_SIZEi size_tpowinien być stosowany w tych przypadkach.
Najnowsze wersje kompilatorów, takie jak GCC 8, ostrzeżą cię, gdy zastosujesz to makro do wskaźnika, więc jest to bezpieczne (istnieją inne metody, aby zapewnić bezpieczeństwo w starszych kompilatorach).
Działa, dzieląc rozmiar w bajtach całej tablicy przez rozmiar każdego elementu.
Przykłady użycia:
void foo(ptrdiff_t nmemb){char buf[nmemb];
fgets(buf, ARRAY_SIZE(buf), stdin);}void bar(ptrdiff_t nmemb){int arr[nmemb];for(ptrdiff_t i =0; i < ARRAY_SSIZE(arr); i++)
arr[i]= i;}
Gdyby te funkcje nie używały tablic, ale zamiast tego otrzymały je jako parametry, poprzedni kod nie skompilowałby się, więc nie byłoby możliwe wystąpienie błędu (biorąc pod uwagę, że użyto najnowszej wersji kompilatora lub zastosowano inną sztuczkę) , i musimy zastąpić wywołanie makra wartością:
void foo(ptrdiff_t nmemb,char buf[nmemb]){
fgets(buf, nmemb, stdin);}void bar(ptrdiff_t nmemb,int arr[nmemb]){for(ptrdiff_t i =0; i < nmemb; i++)
arr[i]= i;}
Rozmiar w bajtach wykorzystywany przez tablicę w pamięci
ARRAY_SIZE jest powszechnie stosowany jako rozwiązanie poprzedniej sprawy, ale ta sprawa rzadko jest pisana bezpiecznie, może dlatego, że jest mniej powszechna.
Typowym sposobem uzyskania tej wartości jest użycie sizeof(arr). Problem: taki sam jak w poprzednim; jeśli zamiast tablicy masz wskaźnik, twój program zwariuje.
Rozwiązanie problemu polega na użyciu tego samego makra, co wcześniej, co wiemy, że jest bezpieczne (przerywa kompilację, jeśli zostanie zastosowane do wskaźnika):
Jak to działa, jest bardzo proste: cofa podział, który ARRAY_SIZEdziała, więc po anulowaniu matematyki otrzymujesz tylko jeden sizeof(arr), ale z dodatkowym bezpieczeństwem ARRAY_SIZEkonstrukcji.
memset() potrzebuje tej wartości jako trzeciego argumentu.
Tak jak poprzednio, jeśli tablica zostanie odebrana jako parametr (wskaźnik), nie zostanie skompilowana i będziemy musieli zastąpić wywołanie makra wartością:
Dzisiaj dowiedziałem się, że nowe ostrzeżenie w GCC działa tylko wtedy, gdy makro jest zdefiniowane w nagłówku, który nie jest nagłówkiem systemowym. Jeśli zdefiniujesz makro w nagłówku, który jest zainstalowany w twoim systemie (zwykle /usr/local/include/lub /usr/include/) ( #include <foo.h>), kompilator NIE wyśle ostrzeżenia (próbowałem GCC 9.3.0).
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a),typeof(b))#define is_array(a)(!is_same_type((a),&(a)[0]))#defineStatic_assert_array(a)_Static_assert(is_array(a),"Not a `[]` !")#define ARRAY_SIZE(arr)( \
{ \
Static_assert_array(arr); \
sizeof(arr)/sizeof((arr)[0]); \
} \
)
Teraz ARRAY_SIZE()jest całkowicie bezpieczny, a zatem wszystkie jego pochodne będą bezpieczne.
Aktualizacja: libbsd zapewnia __arraycount():
Libbsd zapewnia makro __arraycount()w <sys/cdefs.h>, co jest niebezpieczne, ponieważ brakuje mu parę nawiasów, ale możemy dodać te nawiasach siebie, a więc nawet nie trzeba pisać podział w naszym nagłówku (dlaczego chcemy powielić kod, który już istnieje? ). To makro jest zdefiniowane w nagłówku systemowym, więc jeśli go użyjemy, będziemy zmuszeni użyć powyższych makr.
Niektóre systemy zapewniają nitems()w <sys/param.h>zamian, a niektóre zapewniają oba. Powinieneś sprawdzić swój system i użyć tego, który posiadasz, a być może użyć niektórych warunków wstępnych procesora dla zapewnienia przenośności i obsługi obu.
Aktualizacja: Zezwól na użycie makra w zakresie plików:
Niestety ({})rozszerzenia gcc nie można używać w zakresie plików. Aby móc korzystać z makra w zakresie plików, należy zapewnić statyczne potwierdzenie sizeof(struct {}). Następnie pomnóż go, 0aby nie wpłynąć na wynik. Rzutowanie na (int)może być przydatne do symulacji funkcji, która zwraca (int)0(w tym przypadku nie jest to konieczne, ale można ją wykorzystać do innych celów).
Czy mógłbyś wyjaśnić, dlaczego głosowanie negatywne? Wskazuje to rozwiązanie do niebezpiecznych i wspólnej konstrukcji ( sizeof(arr)), które nie są pokazane: w innym miejscu ARRAY_BYTES(arr).
Cacahuete Frito,
2
ARRAY_SIZE jest na tyle powszechny, że można z niego swobodnie korzystać, a ARRAY_BYTES jest bardzo wyraźny w swojej nazwie, powinien być zdefiniowany obok ARRAY_SIZE, aby użytkownik mógł zobaczyć zarówno łatwo, jak i jego użycie, nie sądzę, aby ktokolwiek czytający kod miał wątpliwości co do tego to robi. Chodziło mi o to, aby nie używać prostego sizeof, ale zamiast tego używać tych konstrukcji; jeśli masz ochotę pisać te konstrukcje za każdym razem, prawdopodobnie popełnisz błąd (bardzo częsty, jeśli kopiujesz wklej, a także bardzo powszechny, jeśli piszesz je za każdym razem, ponieważ mają wiele nawiasów) ...
Cacahuete Frito
3
... więc stawiam na główny wniosek: singiel sizeofjest wyraźnie niebezpieczny (przyczyny są w odpowiedzi), i nie używa makr, ale korzysta z konstrukcji, które podałem za każdym razem, jest jeszcze bardziej niebezpieczny, więc jedyną drogą, aby przejść to makra.
Cacahuete Frito,
3
@MarkHarrison Znam różnicę między wskaźnikami i tablicami. Ale były czasy, kiedy miałem funkcję, którą później przekształciłem w małe funkcje, a to, co najpierw było tablicą, później było wskaźnikiem, i to jest jeden punkt, w którym jeśli zapomnisz zmienić rozmiar, wkręcisz go i łatwo nie widać jeden z tych.
Cacahuete Frito
3
@hyde Również, że wiem, że różnica nie oznacza, że wszyscy ją znają, a dlaczego nie użyć czegoś, co w zasadzie usuwa 100% tych błędów? Ten błąd prawie dotarł do Linuksa; dotarł do Linusa, co oznacza, że przeszedł wiele kontroli, a co za tym idzie, istnieje możliwość, że ten sam błąd dostał się do Linuksa w innej części jądra, jak sam mówi.
Cacahuete Frito,
11
„wprowadziłeś subtelny sposób strzelania sobie w stopę”
C Tablice „rodzime” nie przechowują swoich rozmiarów. Dlatego zaleca się zapisanie długości tablicy w osobnej zmiennej / const i przekazywanie jej za każdym razem, gdy przekazujesz tablicę, to znaczy:
NALEŻY zawsze unikać rodzimych tablic (chyba że nie możesz, w takim przypadku uważaj na swoją stopę). Jeśli piszesz w C ++, użyj kontenera „wektorowego” STL . „W porównaniu z tablicami zapewniają prawie taką samą wydajność” i są znacznie bardziej przydatne!
// vector is a template, the <int> means it is a vector of intsvector<int> numbers;// push_back() puts a new value at the end (or back) of the vectorfor(int i =0; i <10; i++)
numbers.push_back(i);// Determine the size of the array
cout << numbers.size();
Zauważ, że działa to tylko w przypadku rzeczywistych tablic, a nie wskaźników, które akurat wskazują na tablice.
David Schwartz
5
Jeśli naprawdę chcesz to zrobić, aby ominąć tablicę, sugeruję zaimplementowanie struktury do przechowywania wskaźnika do typu, którego chcesz tablicę, oraz liczby całkowitej reprezentującej rozmiar tablicy. Następnie możesz przekazać to swoim funkcjom. Po prostu przypisz wartość zmiennej tablicowej (wskaźnik do pierwszego elementu) do tego wskaźnika. Następnie możesz przejść Array.arr[i]do i-tego elementu i użyć, Array.sizeaby uzyskać liczbę elementów w tablicy.
Podałem ci trochę kodu. Nie jest to bardzo przydatne, ale można go rozszerzyć o więcej funkcji. Szczerze mówiąc, jeśli tego właśnie chcesz, powinieneś przestać używać C i używać innego języka z wbudowanymi funkcjami.
/* Absolutely no one should use this...
By the time you're done implementing it you'll wish you just passed around
an array and size to your functions *//* This is a static implementation. You can get a dynamic implementation and
cut out the array in main by using the stdlib memory allocation methods,
but it will work much slower since it will store your array on the heap */#include<stdio.h>#include<string.h>/*
#include "MyTypeArray.h"
*//* MyTypeArray.h
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/typedefstructMyType{int age;char name[20];}MyType;typedefstructMyTypeArray{int size;MyType*arr;}MyTypeArray;MyType new_MyType(int age,char*name);MyTypeArray newMyTypeArray(int size,MyType*first);/*
#endif
End MyTypeArray.h *//* MyTypeArray.c */MyType new_MyType(int age,char*name){MyType d;
d.age = age;
strcpy(d.name, name);return d;}MyTypeArray new_MyTypeArray(int size,MyType*first){MyTypeArray d;
d.size = size;
d.arr = first;return d;}/* End MyTypeArray.c */void print_MyType_names(MyTypeArray d){int i;for(i =0; i < d.size; i++){
printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);}}int main(){/* First create an array on the stack to store our elements in.
Note we could create an empty array with a size instead and
set the elements later. */MyType arr[]={new_MyType(10,"Sam"), new_MyType(3,"Baxter")};/* Now create a "MyTypeArray" which will use the array we just
created internally. Really it will just store the value of the pointer
"arr". Here we are manually setting the size. You can use the sizeof
trick here instead if you're sure it will work with your compiler. */MyTypeArrayarray= new_MyTypeArray(2, arr);/* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
print_MyType_names(array);return0;}
Nie można upvote kodu, który strcpy(d.name, name);nie obsługuje przepełnienia.
chux - Przywróć Monikę
4
Najlepszym sposobem jest zapisanie tych informacji, na przykład w strukturze:
typedefstruct{int*array;int elements;} list_s;
Wdrażaj wszystkie niezbędne funkcje, takie jak tworzenie, niszczenie, sprawdzanie równości i wszystko, czego potrzebujesz. Łatwiej jest przekazać jako parametr.
Funkcja sizeofzwraca liczbę bajtów używanych przez tablicę w pamięci. Jeśli chcesz obliczyć liczbę elementów w tablicy, powinieneś podzielić tę liczbę ze sizeofzmiennym typem tablicy. Powiedzmy, że int array[10];jeśli liczba całkowita typu zmiennego w twoim komputerze to 32 bity (lub 4 bajty), aby uzyskać rozmiar tablicy, wykonaj następujące czynności:
#include<stdio.h>#include<stdlib.h>int main(){int a[10];int*p;
printf("%p\n",(void*)a);
printf("%p\n",(void*)(&a+1));
printf("---- diff----\n");
printf("%zu\n",sizeof(a[0]));
printf("The size of array a is %zu\n",((char*)(&a+1)-(char*)a)/(sizeof(a[0])));return0;};
Oto przykładowy wynik
15492166721549216712---- diff----4The size of array a is 10
Nie głosowałem za głosem, ale to jest jak uderzanie gwoździa cegłą, ponieważ nie zauważyłeś leżącego obok siebie młota. Ponadto ludzie mają skłonność do marszczenia się za używanie niezainicjowanych zmiennych ... ale tutaj myślę, że służy to wystarczająco dobrze celowi.
Dmitri
2
@Dmitri nie są tu dostępne żadne niezainicjowane zmienne
MM
1
Hmmm. Odejmowanie wskaźnika prowadzi do ptrdiff_t. sizeof()powoduje w size_t. C nie określa, która jest szersza lub wyższa / ta sama ranga. Zatem rodzaj ilorazu ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))nie jest na pewno, size_ta zatem drukowanie za pomocą zmoże prowadzić do UB. Wystarczy samo użycie printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);.
chux - Przywróć Monikę
1
(char *)(&a+1)-(char *)anie jest stałą i może być obliczany w czasie wykonywania, nawet przy stałej wielkości a[10]. sizeof(a)/sizeof(a[0])w tym przypadku jest wykonywana stale w czasie kompilacji.
Oprócz udzielonych już odpowiedzi chcę wskazać szczególny przypadek zastosowania
sizeof(a)/sizeof(a[0])
Jeśli ajest albo tablicą char, unsigned charalbo signed charnie trzeba używać sizeofdwa razy, ponieważ zawsze występuje sizeofwyrażenie z jednym operandem tego typu 1.
Cytat z C18,6.5.3.4 / 4:
„ Gdy sizeofzostanie zastosowany do operandu, który ma typ char, unsigned charlub signed char(lub jego kwalifikowaną wersję), wynikiem jest 1.”
Zatem sizeof(a) / sizeof (a[0])byłby równoważny z NUMBER OF ARRAY ELEMENTS / 1if, jeśli ajest tablicą typu char, unsigned charlub signed char. Podział na 1 jest zbędny.
W takim przypadku możesz po prostu skrócić i wykonać:
Prawdopodobnie wolisz nadal stosować makro z podziałem, ponieważ typ może się zmienić w przyszłości (choć być może mało prawdopodobne), a podział jest znany w czasie kompilacji, więc kompilator zoptymalizuje go (jeśli się nie zmieni twój kompilator).
Cacahuete Frito
1
@CacahueteFrito Tak, w międzyczasie też o tym myślałem. Wziąłem to jako dodatkową notatkę w odpowiedzi. Dziękuję Ci.
RobertS wspiera Monikę Cellio
-1
Uwaga: To może dać ci niezdefiniowane zachowanie wskazane przez MM w komentarzu.
int a[10];int size =(*(&a+1)-a);
Aby uzyskać więcej informacji, zobacz
tutaj
i również tutaj .
Jest to technicznie niezdefiniowane zachowanie; *operator nie może być stosowany do wskaźnika past-the-end
MM
3
„nieokreślone zachowanie” oznacza, że Standard C nie definiuje zachowania. Jeśli wypróbujesz to w swoim programie, wszystko może się zdarzyć
MM
@MM, czy mówisz, że *(&a+1) - a;różni się od (&a)[1] - a;powyższego, czy nie oba *(&a+1)i (&a)[1]liczone jako 1 po zakończeniu?
QuentinUK
@QuentinUK twoje dwa wyrażenia są takie same, x[y]jest zdefiniowane jako*(x + (y))
MM
@MM Tak myślałem. Ale druga odpowiedź Arjun Sreedharan ma 38 strzał w górę, a to ma -1. W odpowiedzi Arjun Sreedharan nie ma wzmianki o nieokreślonym zachowaniu.
Odpowiedzi:
Streszczenie:
Pełna odpowiedź:
Aby określić rozmiar tablicy w bajtach, możesz użyć
sizeof
operatora:Na moim komputerze ints mają 4 bajty, więc n ma 68.
Aby określić liczbę elementów w tablicy, możemy podzielić całkowity rozmiar tablicy przez rozmiar elementu tablicy. Możesz to zrobić z tym typem:
i uzyskać prawidłową odpowiedź (68/4 = 17), ale jeśli
a
zmienił się typ , miałbyś paskudny błąd, gdybyś również zapomniał zmienićsizeof(int)
.Tak więc preferowanym dzielnikiem jest
sizeof(a[0])
lub równoważnysizeof(*a)
, rozmiar pierwszego elementu tablicy.Kolejną zaletą jest to, że możesz teraz łatwo sparametryzować nazwę tablicy w makrze i uzyskać:
źródło
ARRAYSIZE
makro zdefiniowane wWinNT.h
(które jest pobierane przez inne nagłówki). Dlatego użytkownicy WinAPI nie muszą definiować własnego makra.static int a[20];
. Ale twój komentarz jest przydatny dla czytelników, którzy mogą nie zdawać sobie sprawy z różnicy między tablicą a wskaźnikiem.sizeof
Droga jest właściwa droga IFF masz do czynienia z tablicami nie odebranych jako parametry. Tablica wysłana jako parametr do funkcji jest traktowana jako wskaźnik, więcsizeof
zwróci rozmiar wskaźnika, zamiast rozmiaru tablicy.Dlatego w funkcjach wewnętrznych ta metoda nie działa. Zamiast tego zawsze przekazuj dodatkowy parametr
size_t size
wskazujący liczbę elementów w tablicy.Test:
Dane wyjściowe (w 64-bitowym systemie operacyjnym Linux):
Dane wyjściowe (w 32-bitowym systemie operacyjnym Windows):
źródło
length of parameter:2
jeśli przekazywany jest tylko wskaźnik do pierwszego elementu tablicy?(sizeof array / sizeof *array)
.Warto zauważyć, że
sizeof
nie pomaga to w przypadku wartości tablicy, która uległa rozkładowi na wskaźnik: mimo że wskazuje na początek tablicy, w przypadku kompilatora jest ona taka sama jak wskaźnik do pojedynczego elementu tej tablicy . Wskaźnik nie „pamięta” niczego innego o tablicy, która została użyta do jego zainicjowania.źródło
char
32 bitami. Wszystko, co mówi standard, to to, że mogą być reprezentowane wartości całkowite od 0 do 127, a jego zakres wynosi co najmniej -127 do 127 (znak jest podpisany) lub 0 do 255 (znak jest niepodpisany).Rozmiar „sztuczki” to najlepszy sposób, jaki znam, z jedną małą, ale (dla mnie, to jest główną wkurzaniem) istotną zmianą w stosowaniu nawiasów.
Jak wyjaśnia wpis w Wikipedii, C
sizeof
nie jest funkcją; to operator . Dlatego nie wymaga nawiasów wokół argumentu, chyba że argument jest nazwą typu. Łatwo to zapamiętać, ponieważ sprawia, że argument wygląda jak rzutowane wyrażenie, które również używa nawiasów.Więc: jeśli masz następujące elementy:
Możesz znaleźć liczbę elementów o takim kodzie:
To dla mnie brzmi o wiele łatwiej niż alternatywa z nawiasami. Popieram także użycie gwiazdki w prawej części działu, ponieważ jest bardziej zwięzła niż indeksowanie.
Oczywiście, to także czas kompilacji, więc nie musisz się martwić podziałem wpływającym na wydajność programu. Więc skorzystaj z tego formularza, gdziekolwiek możesz.
Zawsze najlepiej jest używać sizeof na rzeczywistym obiekcie, gdy go masz, a nie na typie, ponieważ wtedy nie musisz się martwić o popełnienie błędu i podanie niewłaściwego typu.
Załóżmy na przykład, że masz funkcję, która wyprowadza niektóre dane jako strumień bajtów, na przykład przez sieć. Wywołajmy funkcję
send()
i sprawmy, aby jako argumenty przyjmowała wskaźnik do obiektu do wysłania oraz liczbę bajtów w obiekcie. Prototyp staje się:A potem musisz wysłać liczbę całkowitą, więc kodujesz to w ten sposób:
Teraz wprowadziłeś subtelny sposób strzelania sobie w stopę, określając typ
foo
w dwóch miejscach. Jeśli jeden się zmieni, a drugi nie, kod się zepsuje. Dlatego zawsze rób tak:Teraz jesteś chroniony. Jasne, powielasz nazwę zmiennej, ale ma duże prawdopodobieństwo uszkodzenia w sposób, który kompilator może wykryć, jeśli ją zmienisz.
źródło
sizeof(int)
wymaga mniej instrukcji niżsizeof(foo)
?int x = 1+1;
kontraint x = (1+1);
. Tutaj nawiasy są absolutnie wyłącznie estetyczne.sizeof
zawsze będzie stały w C ++ i C89. W przypadku tablic o zmiennej długości C99 można go oceniać w czasie wykonywania.sizeof
może być operatorem, ale zgodnie z Linusem Torvaldsem należy go traktować jako funkcję. Zgadzam się. Przeczytaj jego uzasadnienie tutaj: lkml.org/lkml/2012/7/11/103Sprawdź ten link do wyjaśnienia
źródło
ptrdiff_t
. (Zwykle w systemie 64-bitowym będzie to większy typint
). Nawet jeśli zmieniszint
naptrdiff_t
w tym kodzie, nadal zawiera błąd, jeśliarr
zajmuje więcej niż połowę przestrzeni adresowej./3G
opcją masz podział użytkownika / jądra 3G / 1G, co pozwala mieć rozmiar tablic do 75% wielkości przestrzeni adresowej.foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
za zmienne globalne.buf3[]
deklaracja kończy się niepowodzeniem, ponieważ(&buf1)[1] - buf1
nie jest stała.Możesz użyć operatora sizeof, ale nie będzie on działał dla funkcji, ponieważ weźmie odwołanie do wskaźnika, możesz wykonać następujące czynności, aby znaleźć długość tablicy:
Pierwotnie znaleziony kod: program C do znalezienia liczby elementów w tablicy
źródło
Jeśli znasz typ danych tablicy, możesz użyć czegoś takiego:
Lub jeśli nie znasz typu danych tablicy, możesz użyć czegoś takiego:
Uwaga: Ta funkcja działa tylko wtedy, gdy tablica nie jest zdefiniowana w czasie wykonywania (np. Malloc), a tablica nie jest przekazywana w funkcji. W obu przypadkach
arr
(nazwa tablicy) jest wskaźnikiem.źródło
int noofele = sizeof(arr)/sizeof(int);
jest tylko w połowie lepszy niż kodowanieint noofele = 9;
. Użyciesizeof(arr)
zachowuje elastyczność w przypadku zmiany rozmiaru tablicy. Jednaksizeof(int)
wymaga aktualizacji, jeśli rodzajarr[]
zmiany. Lepszy w użyciu,sizeof(arr)/sizeof(arr[0])
nawet jeśli typ jest dobrze znany. Niejasne, dlaczegoint
dla fornoofele
vs.size_t
, typ zwracany przezsizeof()
.Makro, z
ARRAYELEMENTCOUNT(x)
którego wszyscy korzystają, ocenia niepoprawnie . W rzeczywistości jest to tylko kwestia drażliwa, ponieważ nie można mieć wyrażeń, które skutkowałyby typem „tablicy”.Faktycznie ocenia się jako:
Natomiast
Poprawnie ocenia:
To naprawdę nie ma wiele wspólnego z rozmiarem tablic. Właśnie zauważyłem wiele błędów wynikających z nieobserwowania, jak działa preprocesor C. Zawsze zawijasz parametr makra, w którym nie może być zaangażowane żadne wyrażenie.
To jest poprawne; mój przykład był zły. Ale tak właśnie powinno być. Jak już wspomniałem
p + 1
, skończy jako typ wskaźnika i unieważni całe makro (podobnie jak w przypadku próby użycia makra w funkcji z parametrem wskaźnika).Na koniec dnia, w tym konkretnym przypadku, błąd tak naprawdę nie ma znaczenia (więc po prostu marnuję czas wszystkich; huzzah!), Ponieważ nie masz wyrażeń z rodzajem „tablicy”. Ale tak naprawdę kwestia subtelności oceny preprocesora uważam za ważną.
źródło
(sizeof (x) / sizeof (*x))
?W przypadku tablic wielowymiarowych jest to nieco bardziej skomplikowane. Często ludzie definiują wyraźne stałe makr, tj
Ale te stałe można również ocenić w czasie kompilacji za pomocą sizeof :
Zauważ, że ten kod działa w C i C ++. Do tablic o więcej niż dwóch wymiarach użyj
itp., ad infinitum.
źródło
źródło
array
ma, nie musisz używać,sizeof(array) / sizeof(array[0])
jeśliarray
jest tablicą albochar
,unsigned char
albosigned char
- Cytat z C18,6.5.3.4 / 4: „Gdy sizeof jest stosowane do operandu, który ma typ char, unsigned char lub podpisany char , (lub jego kwalifikowana wersja) wynik to 1. ” W takim przypadku możesz po prostu zrobić,sizeof(array)
jak wyjaśniono w mojej dedykowanej odpowiedzi .Rozmiar tablicy w C:
źródło
size_t size_of_element
jeszczeint
zint n = sizeof (a) / sizeof (a[0]);
niesize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
,int n
jak użyte wint n = sizeof (a) / sizeof (a[0]);
jest niewystarczające (jest to UB). Korzystaniesize_t n = sizeof (a) / sizeof (a[0]);
nie powoduje tego problemu.Radziłbym nigdy nie używać
sizeof
(nawet jeśli można go użyć), aby uzyskać dowolny z dwóch różnych rozmiarów tablicy, albo w liczbie elementów, albo w bajtach, które są dwoma ostatnimi przypadkami, które tutaj pokazuję. Dla każdego z dwóch rozmiarów można użyć makr pokazanych poniżej, aby zwiększyć bezpieczeństwo. Powodem jest ujawnienie opiekunom intencji kodu i odróżnienie gosizeof(ptr)
odsizeof(arr)
pierwszego spojrzenia (które napisane w ten sposób nie jest oczywiste), aby błędy były widoczne dla wszystkich czytających kod.TL; DR:
must_be_array(arr)
(zdefiniowane poniżej) JEST potrzebne, ponieważ-Wsizeof-pointer-div
jest wadliwe (na kwiecień / 2020 r.):Pojawiły się ważne błędy dotyczące tego tematu: https://lkml.org/lkml/2015/9/3/428
Nie zgadzam się z rozwiązaniem, które zapewnia Linus, którym jest nigdy nie używać notacji tablicowej do parametrów funkcji.
Lubię notację tablicową jako dokumentację, że wskaźnik jest używany jako tablica. Oznacza to jednak, że należy zastosować rozwiązanie niezawodne, aby nie można było napisać błędnego kodu.
Z tablicy mamy trzy rozmiary, które chcielibyśmy poznać:
Rozmiar elementów tablicy
Pierwszy jest bardzo prosty i nie ma znaczenia, czy mamy do czynienia z tablicą czy wskaźnikiem, ponieważ robi się to w ten sam sposób.
Przykład użycia:
qsort()
potrzebuje tej wartości jako trzeciego argumentu.W przypadku pozostałych dwóch rozmiarów, które są tematem pytania, chcemy się upewnić, że mamy do czynienia z tablicą, a jeśli nie, zepsujemy kompilację, ponieważ jeśli mamy do czynienia ze wskaźnikiem, otrzymamy nieprawidłowe wartości . Gdy kompilacja zostanie zerwana, będziemy mogli łatwo zobaczyć, że nie mamy do czynienia z tablicą, ale ze wskaźnikiem, i będziemy musieli po prostu napisać kod ze zmienną lub makrem, który przechowuje rozmiar tablica za wskaźnikiem.
Liczba elementów w tablicy
Ten jest najczęstszy i wiele odpowiedzi dostarczyło typowego makra ARRAY_SIZE:
Biorąc pod uwagę, że wynik ARRAY_SIZE jest powszechnie używany z podpisanymi zmiennymi typu
ptrdiff_t
, dobrze jest zdefiniować podpisany wariant tego makra:Tablice z więcej niż
PTRDIFF_MAX
członkami podadzą niepoprawne wartości dla tej podpisanej wersji makra, ale od odczytu C17 :: 6.5.6.9, takie tablice już się bawią ogniem. TylkoARRAY_SIZE
isize_t
powinien być stosowany w tych przypadkach.Najnowsze wersje kompilatorów, takie jak GCC 8, ostrzeżą cię, gdy zastosujesz to makro do wskaźnika, więc jest to bezpieczne (istnieją inne metody, aby zapewnić bezpieczeństwo w starszych kompilatorach).
Działa, dzieląc rozmiar w bajtach całej tablicy przez rozmiar każdego elementu.
Przykłady użycia:
Gdyby te funkcje nie używały tablic, ale zamiast tego otrzymały je jako parametry, poprzedni kod nie skompilowałby się, więc nie byłoby możliwe wystąpienie błędu (biorąc pod uwagę, że użyto najnowszej wersji kompilatora lub zastosowano inną sztuczkę) , i musimy zastąpić wywołanie makra wartością:
Rozmiar w bajtach wykorzystywany przez tablicę w pamięci
ARRAY_SIZE
jest powszechnie stosowany jako rozwiązanie poprzedniej sprawy, ale ta sprawa rzadko jest pisana bezpiecznie, może dlatego, że jest mniej powszechna.Typowym sposobem uzyskania tej wartości jest użycie
sizeof(arr)
. Problem: taki sam jak w poprzednim; jeśli zamiast tablicy masz wskaźnik, twój program zwariuje.Rozwiązanie problemu polega na użyciu tego samego makra, co wcześniej, co wiemy, że jest bezpieczne (przerywa kompilację, jeśli zostanie zastosowane do wskaźnika):
Jak to działa, jest bardzo proste: cofa podział, który
ARRAY_SIZE
działa, więc po anulowaniu matematyki otrzymujesz tylko jedensizeof(arr)
, ale z dodatkowym bezpieczeństwemARRAY_SIZE
konstrukcji.Przykład użycia:
memset()
potrzebuje tej wartości jako trzeciego argumentu.Tak jak poprzednio, jeśli tablica zostanie odebrana jako parametr (wskaźnik), nie zostanie skompilowana i będziemy musieli zastąpić wywołanie makra wartością:
Aktualizacja (23 kwietnia / 2020):
-Wsizeof-pointer-div
jest wadliwa :Dzisiaj dowiedziałem się, że nowe ostrzeżenie w GCC działa tylko wtedy, gdy makro jest zdefiniowane w nagłówku, który nie jest nagłówkiem systemowym. Jeśli zdefiniujesz makro w nagłówku, który jest zainstalowany w twoim systemie (zwykle
/usr/local/include/
lub/usr/include/
) (#include <foo.h>
), kompilator NIE wyśle ostrzeżenia (próbowałem GCC 9.3.0).Więc mamy
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
i chcemy zapewnić bezpieczeństwo. Będziemy potrzebować C11_Static_assert()
i niektórych rozszerzeń GCC: Instrukcje i deklaracje w wyrażeniach , __builtin_types_compatible_p :Teraz
ARRAY_SIZE()
jest całkowicie bezpieczny, a zatem wszystkie jego pochodne będą bezpieczne.Aktualizacja: libbsd zapewnia
__arraycount()
:Libbsd zapewnia makro
__arraycount()
w<sys/cdefs.h>
, co jest niebezpieczne, ponieważ brakuje mu parę nawiasów, ale możemy dodać te nawiasach siebie, a więc nawet nie trzeba pisać podział w naszym nagłówku (dlaczego chcemy powielić kod, który już istnieje? ). To makro jest zdefiniowane w nagłówku systemowym, więc jeśli go użyjemy, będziemy zmuszeni użyć powyższych makr.Niektóre systemy zapewniają
nitems()
w<sys/param.h>
zamian, a niektóre zapewniają oba. Powinieneś sprawdzić swój system i użyć tego, który posiadasz, a być może użyć niektórych warunków wstępnych procesora dla zapewnienia przenośności i obsługi obu.Aktualizacja: Zezwól na użycie makra w zakresie plików:
Niestety
({})
rozszerzenia gcc nie można używać w zakresie plików. Aby móc korzystać z makra w zakresie plików, należy zapewnić statyczne potwierdzeniesizeof(struct {})
. Następnie pomnóż go,0
aby nie wpłynąć na wynik. Rzutowanie na(int)
może być przydatne do symulacji funkcji, która zwraca(int)0
(w tym przypadku nie jest to konieczne, ale można ją wykorzystać do innych celów).źródło
sizeof(arr)
), które nie są pokazane: w innym miejscuARRAY_BYTES(arr)
.sizeof
, ale zamiast tego używać tych konstrukcji; jeśli masz ochotę pisać te konstrukcje za każdym razem, prawdopodobnie popełnisz błąd (bardzo częsty, jeśli kopiujesz wklej, a także bardzo powszechny, jeśli piszesz je za każdym razem, ponieważ mają wiele nawiasów) ...sizeof
jest wyraźnie niebezpieczny (przyczyny są w odpowiedzi), i nie używa makr, ale korzysta z konstrukcji, które podałem za każdym razem, jest jeszcze bardziej niebezpieczny, więc jedyną drogą, aby przejść to makra.„wprowadziłeś subtelny sposób strzelania sobie w stopę”
C Tablice „rodzime” nie przechowują swoich rozmiarów. Dlatego zaleca się zapisanie długości tablicy w osobnej zmiennej / const i przekazywanie jej za każdym razem, gdy przekazujesz tablicę, to znaczy:
NALEŻY zawsze unikać rodzimych tablic (chyba że nie możesz, w takim przypadku uważaj na swoją stopę). Jeśli piszesz w C ++, użyj kontenera „wektorowego” STL . „W porównaniu z tablicami zapewniają prawie taką samą wydajność” i są znacznie bardziej przydatne!
Zobacz: http://www.cplusplus.com/reference/stl/vector/
źródło
enum
deklaracji.źródło
Jeśli naprawdę chcesz to zrobić, aby ominąć tablicę, sugeruję zaimplementowanie struktury do przechowywania wskaźnika do typu, którego chcesz tablicę, oraz liczby całkowitej reprezentującej rozmiar tablicy. Następnie możesz przekazać to swoim funkcjom. Po prostu przypisz wartość zmiennej tablicowej (wskaźnik do pierwszego elementu) do tego wskaźnika. Następnie możesz przejść
Array.arr[i]
do i-tego elementu i użyć,Array.size
aby uzyskać liczbę elementów w tablicy.Podałem ci trochę kodu. Nie jest to bardzo przydatne, ale można go rozszerzyć o więcej funkcji. Szczerze mówiąc, jeśli tego właśnie chcesz, powinieneś przestać używać C i używać innego języka z wbudowanymi funkcjami.
źródło
strcpy(d.name, name);
nie obsługuje przepełnienia.Najlepszym sposobem jest zapisanie tych informacji, na przykład w strukturze:
Wdrażaj wszystkie niezbędne funkcje, takie jak tworzenie, niszczenie, sprawdzanie równości i wszystko, czego potrzebujesz. Łatwiej jest przekazać jako parametr.
źródło
int elements
vssize_t elements
?Funkcja
sizeof
zwraca liczbę bajtów używanych przez tablicę w pamięci. Jeśli chcesz obliczyć liczbę elementów w tablicy, powinieneś podzielić tę liczbę zesizeof
zmiennym typem tablicy. Powiedzmy, żeint array[10];
jeśli liczba całkowita typu zmiennego w twoim komputerze to 32 bity (lub 4 bajty), aby uzyskać rozmiar tablicy, wykonaj następujące czynności:źródło
Możesz użyć
&
operatora. Oto kod źródłowy:Oto przykładowy wynik
źródło
ptrdiff_t
.sizeof()
powoduje wsize_t
. C nie określa, która jest szersza lub wyższa / ta sama ranga. Zatem rodzaj ilorazu((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
nie jest na pewno,size_t
a zatem drukowanie za pomocąz
może prowadzić do UB. Wystarczy samo użycieprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
.(char *)(&a+1)-(char *)a
nie jest stałą i może być obliczany w czasie wykonywania, nawet przy stałej wielkościa[10]
.sizeof(a)/sizeof(a[0])
w tym przypadku jest wykonywana stale w czasie kompilacji.Najprostsza odpowiedź:
źródło
Bardziej eleganckim rozwiązaniem będzie
źródło
Oprócz udzielonych już odpowiedzi chcę wskazać szczególny przypadek zastosowania
Jeśli
a
jest albo tablicąchar
,unsigned char
albosigned char
nie trzeba używaćsizeof
dwa razy, ponieważ zawsze występujesizeof
wyrażenie z jednym operandem tego typu1
.Cytat z C18,6.5.3.4 / 4:
Zatem
sizeof(a) / sizeof (a[0])
byłby równoważny zNUMBER OF ARRAY ELEMENTS / 1
if, jeślia
jest tablicą typuchar
,unsigned char
lubsigned char
. Podział na 1 jest zbędny.W takim przypadku możesz po prostu skrócić i wykonać:
Na przykład:
Jeśli potrzebujesz dowodu, oto link do GodBolt .
Niemniej jednak dział utrzymuje bezpieczeństwo, jeśli typ znacznie się zmieni (chociaż przypadki te są rzadkie).
źródło
Uwaga: To może dać ci niezdefiniowane zachowanie wskazane przez MM w komentarzu.
Aby uzyskać więcej informacji, zobacz tutaj i również tutaj .
źródło
*
operator nie może być stosowany do wskaźnika past-the-end*(&a+1) - a;
różni się od(&a)[1] - a;
powyższego, czy nie oba*(&a+1)
i(&a)[1]
liczone jako 1 po zakończeniu?x[y]
jest zdefiniowane jako*(x + (y))