Herzlichen Glückwunsch zum Erreichen von 1000 Upvotes!
SS Anne
Antworten:
1260
Zusammenfassung:
int a[17];size_t n =sizeof(a)/sizeof(a[0]);
Vollständige Antwort:
Um die Größe Ihres Arrays in Bytes zu bestimmen, können Sie den sizeof
Operator verwenden:
int a[17];size_t n =sizeof(a);
Auf meinem Computer sind Ints 4 Bytes lang, also ist n 68.
Um die Anzahl der Elemente im Array zu bestimmen, können wir die Gesamtgröße des Arrays durch die Größe des Array-Elements teilen. Sie können dies mit dem Typ wie folgt tun:
int a[17];size_t n =sizeof(a)/sizeof(int);
und erhalten Sie die richtige Antwort (68/4 = 17), aber wenn sich die Art der
aÄnderung ändert, hätten Sie einen bösen Fehler, wenn Sie vergessen hätten, auch die zu ändern sizeof(int).
Der bevorzugte Divisor ist also sizeof(a[0])oder das Äquivalent sizeof(*a)die Größe des ersten Elements des Arrays.
int a[17];size_t n =sizeof(a)/sizeof(a[0]);
Ein weiterer Vorteil ist, dass Sie den Array-Namen jetzt einfach in einem Makro parametrisieren und erhalten können:
#define NELEMS(x)(sizeof(x)/sizeof((x)[0]))int a[17];size_t n = NELEMS(a);
Der generierte Code ist identisch, da der Compiler den Typ von * int_arr zur Kompilierungszeit kennt (und daher den Wert von sizeof (* int_arr)). Es wird eine Konstante sein, und der Compiler kann entsprechend optimieren.
Mark Harrison
10
Dies sollte bei allen Compilern der Fall sein, da die Ergebnisse von sizeof als Konstante für die Kompilierungszeit definiert sind.
Mark Harrison
451
Wichtig : Hören Sie hier nicht auf zu lesen, lesen Sie die nächste Antwort! Dies funktioniert nur für Arrays auf dem Stapel , z. B. wenn Sie malloc () verwenden oder auf einen Funktionsparameter zugreifen, haben Sie kein Glück. Siehe unten.
Markus
7
Für die Windows-API-Programmierung in C oder C ++ ist das ARRAYSIZEMakro definiert WinNT.h(das von anderen Headern abgerufen wird ). WinAPI-Benutzer müssen also kein eigenes Makro definieren.
Lumi
17
@ Markus funktioniert für jede Variable, die einen Array-Typ hat; Dies muss nicht "auf dem Stapel" sein. ZB static int a[20];. Ihr Kommentar ist jedoch für Leser nützlich, die den Unterschied zwischen einem Array und einem Zeiger möglicherweise nicht erkennen.
MM
808
Der sizeofWeg ist der richtige, wenn Sie mit Arrays arbeiten, die nicht als Parameter empfangen wurden. Ein Array, das als Parameter an eine Funktion gesendet wird, wird als Zeiger behandelt, sodass sizeofdie Größe des Zeigers anstelle der Größe des Arrays zurückgegeben wird.
Daher funktioniert diese Methode innerhalb von Funktionen nicht. Übergeben Sie stattdessen immer einen zusätzlichen Parameter size_t size, der die Anzahl der Elemente im Array angibt.
Prüfung:
#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])));}
Ausgabe (in einem 64-Bit-Linux-Betriebssystem):
sizeof of array:28sizeof of parameter:8Length of array:7Length of parameter:2
Ausgabe (in einem 32-Bit-Windows-Betriebssystem):
sizeof of array:28sizeof of parameter:4Length of array:7Length of parameter:1
Warum ist es so, length of parameter:2wenn nur ein Zeiger auf das 1. Array-Element übergeben wird?
Bbvarghe
16
@Bbvarghe Das liegt daran, dass Zeiger in 64-Bit-Systemen 8 Byte (sizeof (intArray)) haben, Ints aber (normalerweise) noch 4 Byte lang sind (sizeof (intArray [0])).
Elideb
13
@Pacerier: Es gibt keinen korrekten Code - die übliche Lösung besteht darin, die Länge zusammen mit dem Array als separates Argument zu übergeben.
Jean Hominal
10
Warten Sie, damit Sie nicht direkt über einen Zeiger auf das Array zugreifen und dessen Größe sehen können. Neu bei C hier.
Sudo
7
@ Michael Trouw: Sie können die Operatorsyntax verwenden, wenn Sie sich dadurch besser fühlen : (sizeof array / sizeof *array).
Chqrlie
134
Es ist erwähnenswert, dass sizeofdies nicht hilfreich ist, wenn es sich um einen Array-Wert handelt, der in einen Zeiger verfallen ist: Obwohl er auf den Anfang eines Arrays zeigt, ist er für den Compiler dasselbe wie ein Zeiger auf ein einzelnes Element dieses Arrays . Ein Zeiger "erinnert" sich an nichts anderes über das Array, mit dem es initialisiert wurde.
int a[10];int* p = a;
assert(sizeof(a)/sizeof(a[0])==10);
assert(sizeof(p)==sizeof(int*));
assert(sizeof(*p)==sizeof(int));
@ Magnus: Der Standard definiert sizeof als die Anzahl der Bytes im Objekt ergebend und diese sizeof (char) ist immer eins. Die Anzahl der Bits in einem Byte ist implementierungsspezifisch. Edit: ANSI C ++ Standard Abschnitt 5.3.3 Sizeof: "Der Operator sizeof gibt die Anzahl der Bytes in der Objektdarstellung seines Operanden an. [...] sizeof (char), sizeof (signiertes Zeichen) und sizeof (vorzeichenloses Zeichen) sind 1; das Ergebnis der Größe von, das auf einen anderen Grundtyp angewendet wird, ist implementierungsdefiniert. "
Skizz
Abschnitt 1.6 Das C ++ - Speichermodell: "Die grundlegende Speichereinheit im C ++ - Speichermodell ist das Byte. Ein Byte ist mindestens groß genug, um ein Mitglied des grundlegenden Ausführungszeichensatzes aufzunehmen, und besteht aus einer zusammenhängenden Folge von Bits, der Zahl davon ist implementierungsdefiniert. "
Skizz
2
Ich erinnere mich, dass der CRAY C mit char32 Bit hatte. Der Standard besagt lediglich, dass ganzzahlige Werte von 0 bis 127 dargestellt werden können und ihr Bereich mindestens entweder -127 bis 127 (Zeichen ist signiert) oder 0 bis 255 (Zeichen ist nicht signiert) beträgt.
vonbrand
5
Dies ist eine hervorragende Antwort. Ich möchte kommentieren, dass alle oben genannten Behauptungen als WAHR bewertet werden.
Javad
49
Die Größe des "Tricks" ist der beste Weg, den ich kenne, mit einer kleinen, aber (für mich ist dies ein großer Ärger mit Haustieren) wichtigen Änderung in der Verwendung von Klammern.
Wie der Wikipedia-Eintrag deutlich macht, ist C sizeofkeine Funktion; Es ist ein Operator . Daher ist für das Argument keine Klammer erforderlich, es sei denn, das Argument ist ein Typname. Dies ist leicht zu merken, da das Argument dadurch wie ein Cast-Ausdruck aussieht, der auch Klammern verwendet.
Also: Wenn Sie Folgendes haben:
int myArray[10];
Sie können die Anzahl der Elemente mit Code wie folgt finden:
size_t n =sizeof myArray /sizeof*myArray;
Das liest sich für mich viel einfacher als die Alternative in Klammern. Ich bevorzuge auch die Verwendung des Sternchens im rechten Teil der Abteilung, da es prägnanter ist als die Indizierung.
Natürlich ist dies auch alles Kompilierungszeit, sodass Sie sich keine Sorgen über die Aufteilung machen müssen, die sich auf die Leistung des Programms auswirkt. Verwenden Sie dieses Formular also, wo immer Sie können.
Es ist immer am besten, sizeof für ein tatsächliches Objekt zu verwenden, wenn Sie eines haben, und nicht für einen Typ, da Sie sich dann nicht darum kümmern müssen, einen Fehler zu machen und den falschen Typ anzugeben.
Angenommen, Sie haben eine Funktion, die einige Daten als Bytestrom ausgibt, beispielsweise über ein Netzwerk. Rufen wir die Funktion auf send()und lassen sie als Argumente einen Zeiger auf das zu sendende Objekt und die Anzahl der Bytes im Objekt verwenden. So wird der Prototyp:
void send(constvoid*object,size_t size);
Und dann müssen Sie eine Ganzzahl senden, damit Sie sie wie folgt codieren:
int foo =4711;
send(&foo,sizeof(int));
Jetzt haben Sie eine subtile Methode eingeführt, mit der Sie sich in den Fuß schießen können, indem Sie den Typ fooan zwei Stellen angeben. Wenn sich einer ändert, der andere jedoch nicht, wird der Code unterbrochen. Also mach es immer so:
send(&foo,sizeof foo);
Jetzt bist du geschützt. Sicher, Sie duplizieren den Namen der Variablen, aber das hat eine hohe Wahrscheinlichkeit, dass der Compiler ihn erkennt, wenn Sie ihn ändern.
Übrigens, sind sie identische Anweisungen auf Prozessorebene? Benötigt sizeof(int)weniger Anweisungen als sizeof(foo)?
Pacerier
@ Pacerier: Nein, sie sind identisch. Denken Sie an int x = 1+1;versus int x = (1+1);. Klammern sind hier rein ästhetisch.
Quetzalcoatl
@Aidiakapi Das stimmt nicht, betrachten Sie C99 VLAs.
Entspannen Sie am
@unwind Danke, ich stehe korrigiert. Um meinen Kommentar zu korrigieren, sizeofwird in C ++ und C89 immer konstant sein. Mit den Arrays variabler Länge von C99 kann es zur Laufzeit ausgewertet werden.
Aidiakapi
2
sizeofkann ein Operator sein, sollte aber gemäß Linus Torvalds als Funktion behandelt werden. Genau. Lesen Sie seine Begründung hier: lkml.org/lkml/2012/7/11/103
Kleiner Nitpick: Das Ergebnis der Zeigersubtraktion hat den Typ ptrdiff_t. (In der Regel ist dies auf einem 64-Bit-System ein größerer Typ als int). Auch wenn Sie ändern , intum ptrdiff_tin diesem Code, es hat immer noch einen Fehler , wenn in arrAnspruch nimmt mehr als die Hälfte des Adressraumes.
MM
2
@MM Ein weiterer kleiner Trottel: Abhängig von Ihrer Systemarchitektur ist der Adressraum bei weitem nicht so groß wie die Zeigergröße auf den meisten Systemen. Windows beispielsweise beschränkt den Adressraum für 64-Bit-Anwendungen auf 8 TB oder 44 Bit. Selbst wenn Sie beispielsweise ein Array haben, das größer als die Hälfte Ihres Adressraums von 4,1 TB ist, ist dies kein Fehler. Nur wenn Ihr Adressraum auf diesen Systemen 63 Bit überschreitet, kann es sogar zu einem solchen Fehler kommen. Mach dir im Allgemeinen keine Sorgen.
Aidiakapi
1
@Aidiakapi unter 32-Bit x86 Linux oder unter Windows mit /3GOption Sie haben 3G / 1G Benutzer / Kernel Split, wodurch Sie Arrays Größe bis zu 75% der Adressraumgröße haben können.
Ruslan
1
Betrachten Sie foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];als globale Variablen. buf3[]Deklaration schlägt fehl, da (&buf1)[1] - buf1es sich nicht um eine Konstante handelt.
chux
2
Dies ist ein technisch undefiniertes Verhalten, da der Standard eine Dereferenzierung nach dem Ende eines Arrays ausdrücklich nicht zulässt (auch wenn Sie nicht versuchen, den gespeicherten Wert zu lesen)
MM
26
Sie können den Operator sizeof verwenden, er funktioniert jedoch nicht für Funktionen, da er die Referenz des Zeigers verwendet. Sie können Folgendes tun, um die Länge eines Arrays zu ermitteln:
Wenn Sie den Datentyp des Arrays kennen, können Sie Folgendes verwenden:
int arr[]={23,12,423,43,21,43,65,76,22};int noofele =sizeof(arr)/sizeof(int);
Wenn Sie den Datentyp des Arrays nicht kennen, können Sie Folgendes verwenden:
noofele =sizeof(arr)/sizeof(arr[0]);
Hinweis: Diese Funktion funktioniert nur, wenn das Array zur Laufzeit nicht definiert ist (wie malloc) und das Array nicht in einer Funktion übergeben wird. In beiden Fällen ist arr(Array-Name) ein Zeiger.
int noofele = sizeof(arr)/sizeof(int);ist nur halbwegs besser als das Codieren int noofele = 9;. Die Verwendung sizeof(arr)behält die Flexibilität bei, falls sich die Arraygröße ändert. Benötigt jedoch sizeof(int)ein Update, sollte sich die Art der arr[]Änderung ändern. Besser zu verwenden, sizeof(arr)/sizeof(arr[0])auch wenn der Typ bekannt ist. Unklar, warum intfür noofelevs. verwendet wird size_t, der Typ, der von zurückgegeben wird sizeof().
chux - Monica
19
Das Makro ARRAYELEMENTCOUNT(x), das jeder verwendet, wird falsch ausgewertet . Dies ist realistisch gesehen nur eine heikle Angelegenheit, da Sie keine Ausdrücke haben können, die zu einem Array-Typ führen.
Dies hat wirklich nicht viel mit der expliziten Größe von Arrays zu tun. Ich habe gerade viele Fehler bemerkt, weil ich nicht wirklich beobachtet habe, wie der C-Präprozessor funktioniert. Sie schließen immer den Makroparameter ein, an dem möglicherweise kein Ausdruck beteiligt ist.
Das ist richtig; Mein Beispiel war schlecht. Aber genau das sollte passieren. Wie bereits erwähnt, p + 1wird dies als Zeigertyp enden und das gesamte Makro ungültig machen (genau wie wenn Sie versucht haben, das Makro in einer Funktion mit einem Zeigerparameter zu verwenden).
Letztendlich spielt der Fehler in diesem speziellen Fall keine Rolle (also verschwende ich nur die Zeit aller; huzzah!), Weil Sie keine Ausdrücke mit einer Art 'Array' haben. Aber wirklich der Punkt über Subprozessor-Evaluierungs-Untertitel ist meiner Meinung nach wichtig.
Danke für die Erklärung. Die Originalversion führt zu einem Fehler bei der Kompilierung. Clang meldet "Der tiefgestellte Wert ist kein Array, Zeiger oder Vektor". Dies scheint in diesem Fall vorzuziehen, obwohl Ihre Kommentare zur Auswertungsreihenfolge in Makros gut aufgenommen werden.
Mark Harrison
1
Ich hatte die Compiler-Beschwerde nicht als automatische Benachrichtigung eines falschen Typs angesehen. Vielen Dank!
3
Gibt es einen Grund, nicht zu verwenden (sizeof (x) / sizeof (*x))?
Seriousdev
16
Für mehrdimensionale Arrays ist es etwas komplizierter. Oft definieren Menschen explizite Makrokonstanten, dh
Abhängig von der Art arrayhat, die Sie nicht verwenden müssen , sizeof(array) / sizeof(array[0])wenn arrayein Array von entweder char, unsigned charoder signed char- Zitat aus C18,6.5.3.4 / 4: „Wenn sizeof auf einen Operanden angewandt wird, den Typ char, unsigned char hat, oder signed char , (oder eine qualifizierte Version davon) das Ergebnis ist 1. " In diesem Fall können Sie einfach das tun sizeof(array), was in meiner speziellen Antwort erläutert wurde .
RobertS unterstützt Monica Cellio
15
Größe eines Arrays in 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
Neugierig, dass Code size_t size_of_elementnoch intmit int n = sizeof (a) / sizeof (a[0]); und nichtsize_t n = sizeof (a) / sizeof (a[0]);
chux - Reinstate Monica
1
Hallo @Yogeesh HT, können Sie bitte den Zweifel von chux beantworten. Ich bin auch sehr neugierig zu wissen, wie int n = Größe von (a) / Größe von (a [0]) die Länge des Arrays angibt und warum wir size_t nicht für die Länge des Arrays verwenden. Kann jemand darauf antworten?
Gehirn
1
@Brain sizeof (a) gibt die Größe aller im Array vorhandenen Elemente an. A sizeof (a [0]) gibt die Größe der ersten Elemente an. Angenommen, a = {1,2,3,4,5}; sizeof (a) = 20bytes (wenn sizeof (int) = 4bytes 5 multiplizieren), sizeof (a [0]) = 4bytes, also 20/4 = 5, dh
Anzahl
2
@YogeeshHT Für sehr große Arrays wie char a[INT_MAX + 1u];, int nwie in verwendet, int n = sizeof (a) / sizeof (a[0]);ist nicht ausreichend (es ist UB). Bei der Verwendung size_t n = sizeof (a) / sizeof (a[0]);tritt dieses Problem nicht auf.
chux
13
Ich würde raten, niemals sizeofeine der beiden unterschiedlichen Größen eines Arrays zu verwenden (auch wenn es verwendet werden kann), entweder in Anzahl der Elemente oder in Bytes. Dies sind die letzten beiden Fälle, die ich hier zeige. Für jede der beiden Größen können die unten gezeigten Makros verwendet werden, um die Sicherheit zu erhöhen. Der Grund ist offensichtlich die Absicht des Codes zu Maintainer zu machen, und die Differenz sizeof(ptr)von sizeof(arr)auf dem ersten Blick (die auf diese Weise geschrieben ist nicht offensichtlich), so dass Fehler sind dann offensichtlich für jeden , den Code zu lesen.
Ich bin nicht einverstanden mit der von Linus bereitgestellten Lösung, bei der niemals die Array-Notation für Parameter von Funktionen verwendet wird.
Ich mag die Array-Notation als Dokumentation, dass ein Zeiger als Array verwendet wird. Dies bedeutet jedoch, dass eine narrensichere Lösung angewendet werden muss, damit kein fehlerhafter Code geschrieben werden kann.
Aus einem Array haben wir drei Größen, die wir vielleicht wissen möchten:
Die Größe der Elemente des Arrays
Die Anzahl der Elemente im Array
Die Größe in Bytes, die das Array im Speicher verwendet
Die Größe der Elemente des Arrays
Der erste ist sehr einfach und es spielt keine Rolle, ob es sich um ein Array oder einen Zeiger handelt, da dies auf die gleiche Weise erfolgt.
qsort() benötigt diesen Wert als drittes Argument.
Für die beiden anderen Größen, die Gegenstand der Frage sind, möchten wir sicherstellen, dass es sich um ein Array handelt, und die Kompilierung unterbrechen, wenn nicht, da wir bei einem Zeiger falsche Werte erhalten . Wenn die Kompilierung fehlerhaft ist, können wir leicht erkennen, dass es sich nicht um ein Array, sondern um einen Zeiger handelte, und wir müssen den Code nur mit einer Variablen oder einem Makro schreiben, in dem die Größe des Arrays gespeichert ist Array hinter dem Zeiger.
Die Anzahl der Elemente im Array
Dies ist die häufigste und viele Antworten haben Ihnen das typische Makro ARRAY_SIZE geliefert:
Da das Ergebnis von ARRAY_SIZE häufig mit vorzeichenbehafteten Variablen vom Typ verwendet wird ptrdiff_t, empfiehlt es sich, eine vorzeichenbehaftete Variante dieses Makros zu definieren:
Arrays mit mehr als PTRDIFF_MAXMitgliedern geben ungültige Werte für diese signierte Version des Makros an, aber nach dem Lesen von C17 :: 6.5.6.9 spielen solche Arrays bereits mit dem Feuer. Nur ARRAY_SIZEund size_tsollte in diesen Fällen verwendet werden.
Neuere Versionen von Compilern wie GCC 8 warnen Sie, wenn Sie dieses Makro auf einen Zeiger anwenden, damit es sicher ist (es gibt andere Methoden, um es mit älteren Compilern sicher zu machen).
Es funktioniert, indem die Größe des gesamten Arrays in Bytes durch die Größe jedes Elements geteilt wird.
Anwendungsbeispiele:
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;}
Wenn diese Funktionen keine Arrays verwenden, sondern sie stattdessen als Parameter erhalten würden, würde der frühere Code nicht kompiliert, sodass es unmöglich wäre, einen Fehler zu haben (vorausgesetzt, dass eine neuere Compilerversion verwendet wird oder ein anderer Trick verwendet wird). und wir müssen den Makroaufruf durch den Wert ersetzen:
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;}
Die Größe in Bytes, die das Array im Speicher verwendet
ARRAY_SIZE wird häufig als Lösung für den vorherigen Fall verwendet, aber dieser Fall wird selten sicher geschrieben, möglicherweise weil er weniger häufig ist.
Der übliche Weg, um diesen Wert zu erhalten, ist die Verwendung sizeof(arr). Das Problem: das gleiche wie beim vorherigen; Wenn Sie einen Zeiger anstelle eines Arrays haben, wird Ihr Programm verrückt.
Die Lösung des Problems besteht darin, dasselbe Makro wie zuvor zu verwenden, von dem wir wissen, dass es sicher ist (es bricht die Kompilierung ab, wenn es auf einen Zeiger angewendet wird):
Wie es funktioniert, ist sehr einfach: Es macht die Teilung rückgängig, die dies ARRAY_SIZEtut. Nach mathematischen Stornierungen erhalten Sie also nur eine sizeof(arr), aber mit der zusätzlichen Sicherheit der ARRAY_SIZEKonstruktion.
Heute habe ich herausgefunden, dass die neue Warnung in GCC nur funktioniert, wenn das Makro in einem Header definiert ist, der kein Systemheader ist. Wenn Sie das Makro in einem Header definieren, der in Ihrem System installiert ist (normalerweise /usr/local/include/oder /usr/include/) ( #include <foo.h>), gibt der Compiler KEINE Warnung aus (ich habe GCC 9.3.0 ausprobiert).
#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]); \
} \
)
Jetzt ARRAY_SIZE()ist es völlig sicher, und daher sind alle seine Derivate sicher.
Update: libbsd bietet __arraycount():
Libbsd stellt das Makro __arraycount()in bereit <sys/cdefs.h>, was unsicher ist, da es keine Klammern enthält, aber wir können diese Klammern selbst hinzufügen, und daher müssen wir nicht einmal die Unterteilung in unseren Header schreiben (warum sollten wir bereits vorhandenen Code duplizieren? ). Dieses Makro ist in einem Systemheader definiert. Wenn wir es also verwenden, müssen wir die obigen Makros verwenden.
Einige Systeme bieten nitems()in <sys/param.h>statt und einige Systeme bieten beides. Sie sollten Ihr System überprüfen und das System verwenden, das Sie haben, und möglicherweise einige Präprozessorbedingungen für die Portabilität verwenden und beide unterstützen.
Update: Ermöglicht die Verwendung des Makros im Dateibereich:
Leider kann die ({})Erweiterung gcc im Dateibereich nicht verwendet werden. Um das Makro im Dateibereich verwenden zu können, muss sich die statische Zusicherung darin befinden sizeof(struct {}). Multiplizieren Sie es dann mit 0, um das Ergebnis nicht zu beeinflussen. Eine Umwandlung in (int)kann gut sein, um eine zurückgegebene Funktion zu simulieren (int)0(in diesem Fall ist sie nicht erforderlich, kann dann aber für andere Zwecke wiederverwendet werden).
Würde es Ihnen etwas ausmachen zu erklären, warum das Downvote? Dies zeigt eine Lösung für eine unsichere und häufig vorkommende Konstruktion ( sizeof(arr)), die an keiner anderen Stelle gezeigt wird : ARRAY_BYTES(arr).
Cacahuete Frito
2
ARRAY_SIZE ist häufig genug, um frei verwendet zu werden, und ARRAY_BYTES ist in seinem Namen sehr explizit und sollte neben ARRAY_SIZE definiert werden, damit ein Benutzer beide leicht sehen kann. Aufgrund seiner Verwendung glaube ich nicht, dass jemand, der den Code liest, Zweifel daran hat, was es tut. Was ich damit gemeint habe, ist, keine einfache zu verwenden sizeof, sondern stattdessen diese Konstruktionen zu verwenden; Wenn Sie diese Konstruktionen jedes Mal schreiben
möchten
3
..., also stehe ich zu der Hauptschlussfolgerung: Eine einzelne sizeofist eindeutig unsicher (Gründe sind in der Antwort enthalten), und nicht Makros zu verwenden, sondern die von mir bereitgestellten Konstruktionen jedes Mal zu verwenden, ist noch unsicherer, also der einzige Weg ist Makros.
Cacahuete Frito
3
@ MarkHarrison Ich kenne den Unterschied zwischen Zeigern und Arrays. Aber es gab Zeiten, in denen ich eine Funktion hatte, die ich später in kleine Funktionen umgestaltete, und was zuerst ein Array, später ein Zeiger war, und das ist ein Punkt, an dem man, wenn man vergisst, die Größe zu ändern, sie verschraubt und es leicht zu sehen ist eine von diesen.
Cacahuete Frito
3
@hyde Auch dass ich den Unterschied kenne, bedeutet nicht, dass jeder den Unterschied kennt, und warum nicht etwas verwenden, das im Grunde 100% dieser Fehler beseitigt? Dieser Fehler hat es fast in Linux geschafft; es ist bei Linus angekommen, was bedeutet, dass es eine Menge Prüfung bestanden hat, und was auch bedeutet, dass derselbe Fehler es in Linux in einem anderen Teil des Kernels geschafft hat, wie er sagt.
Cacahuete Frito
11
"Sie haben eine subtile Art eingeführt, sich in den Fuß zu schießen."
C 'native' Arrays speichern ihre Größe nicht. Es wird daher empfohlen, die Länge des Arrays in einer separaten Variablen / const zu speichern und sie immer dann zu übergeben, wenn Sie das Array übergeben, d. H.
Sie sollten native Arrays immer vermeiden (es sei denn, Sie können sich in diesem Fall nicht um Ihren Fuß kümmern). Wenn Sie C ++ schreiben, verwenden Sie den 'Vektor'-Container der STL . "Im Vergleich zu Arrays bieten sie fast die gleiche Leistung" und sind weitaus nützlicher!
// 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();
Beachten Sie, dass dies nur für tatsächliche Arrays funktioniert, nicht für Zeiger, die zufällig auf Arrays verweisen.
David Schwartz
5
Wenn Sie dies wirklich tun möchten, um Ihr Array zu umgehen, empfehle ich, eine Struktur zu implementieren, um einen Zeiger auf den Typ zu speichern, von dem Sie ein Array und eine Ganzzahl, die die Größe des Arrays darstellt, möchten. Dann können Sie das an Ihre Funktionen weitergeben. Weisen Sie diesem Zeiger einfach den Wert der Array-Variablen (Zeiger auf das erste Element) zu. Dann können Sie Array.arr[i]das i-te Element abrufen und Array.sizedie Anzahl der Elemente im Array ermitteln.
Ich habe einen Code für Sie eingefügt. Es ist nicht sehr nützlich, aber Sie könnten es um weitere Funktionen erweitern. Um ehrlich zu sein, sollten Sie, wenn dies die gewünschten Dinge sind, die Verwendung von C beenden und eine andere Sprache mit diesen integrierten Funktionen verwenden.
/* 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;}
Code, der strcpy(d.name, name);ohne Behandlung des Überlaufs funktioniert, kann nicht hochgestuft werden .
chux
4
Am besten speichern Sie diese Informationen beispielsweise in einer Struktur:
typedefstruct{int*array;int elements;} list_s;
Implementieren Sie alle erforderlichen Funktionen wie Erstellen, Zerstören, Überprüfen der Gleichheit und alles andere, was Sie benötigen. Es ist einfacher, als Parameter zu übergeben.
Irgendein Grund für int elementsvs. size_t elements?
chux
3
Die Funktion sizeofgibt die Anzahl der Bytes zurück, die von Ihrem Array im Speicher verwendet werden. Wenn Sie die Anzahl der Elemente in Ihrem Array berechnen möchten, sollten Sie diese Anzahl durch den sizeofVariablentyp des Arrays teilen . Angenommen int array[10];, wenn die Ganzzahl vom Variablentyp in Ihrem Computer 32 Bit (oder 4 Byte) beträgt, sollten Sie Folgendes tun, um die Größe Ihres Arrays zu ermitteln:
Sie können den &Operator verwenden. Hier ist der Quellcode:
#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;};
Hier ist die Beispielausgabe
15492166721549216712---- diff----4The size of array a is 10
Ich habe nicht abgelehnt, aber das ist, als würde man einen Nagel mit einem Ziegelstein schlagen, weil man keinen Hammer neben sich bemerkt hat. Außerdem neigen die Leute dazu, nicht initialisierte Variablen zu verwenden ... aber hier denke ich, dass es Ihrem Zweck gut genug dient.
Dmitri
2
@Dmitri hier werden keine nicht initialisierten Variablen abgerufen
MM
1
Hmmm. Zeigersubtraktion führt zu ptrdiff_t. sizeof()führt zu size_t. C definiert nicht , welcher breiter oder höher / gleicher Rang ist. Die Art des Quotienten ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))ist also nicht sicher size_tund daher kann das Drucken mit zzu UB führen. Einfach zu benutzen printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);ist ausreichend.
chux
1
(char *)(&a+1)-(char *)aist keine Konstante und kann zur Laufzeit auch bei fester Größe berechnet werden a[10]. sizeof(a)/sizeof(a[0])wird in diesem Fall konstant zur Kompilierungszeit ausgeführt.
Neben den bereits gegebenen Antworten möchte ich auf einen Sonderfall durch die Verwendung von hinweisen
sizeof(a)/sizeof(a[0])
Wenn aes sich entweder um ein Array von charhandelt unsigned charoder signed charSie es nicht sizeofzweimal sizeofverwenden müssen, ergibt sich immer ein Ausdruck mit einem Operanden dieser Typen 1.
Zitat aus C18,6.5.3.4 / 4:
„ Wenn sizeofauf einen Operanden angewandt , die Aktivität hat char, unsigned charoder signed char, (oder eine qualifizierte Version davon) das Ergebnis ist 1.“
Somit sizeof(a) / sizeof (a[0])wäre äquivalent , NUMBER OF ARRAY ELEMENTS / 1wenn aein Array des Typs char, unsigned charoder signed char. Die Division durch 1 ist redundant.
In diesem Fall können Sie einfach abkürzen und Folgendes tun:
sizeof(a)
Zum Beispiel:
char a[10];size_t length =sizeof(a);
Wenn Sie einen Beweis wollen, finden Sie hier einen Link zu GodBolt .
Trotzdem behält die Abteilung die Sicherheit bei, wenn sich der Typ erheblich ändert (obwohl diese Fälle selten sind).
Sie ziehen es wahrscheinlich vor, weiterhin ein Makro mit der Division anzuwenden, da sich der Typ in Zukunft möglicherweise ändert (obwohl dies möglicherweise unwahrscheinlich ist) und die Division zur Kompilierungszeit bekannt ist, sodass der Compiler sie wegoptimiert (falls dies nicht geändert wird) Ihr Compiler).
Cacahuete Frito
1
@CacahueteFrito Ja, darüber habe ich inzwischen auch nachgedacht. Ich nahm es als Randnotiz in die Antwort. Vielen Dank.
RobertS unterstützt Monica Cellio
-1
Hinweis: Dieser kann Ihnen undefiniertes Verhalten geben, wie von MM im Kommentar angegeben.
Dies ist technisch undefiniertes Verhalten; Der *Operator darf nicht auf einen Past-the-End-Zeiger angewendet werden
MM
3
"undefiniertes Verhalten" bedeutet, dass der C-Standard das Verhalten nicht definiert. Wenn Sie es in Ihrem Programm versuchen, kann alles passieren
MM
@MM sagen Sie, *(&a+1) - a;ist anders als (&a)[1] - a;oben, nicht beide *(&a+1)und (&a)[1]zählen als 1 nach dem Ende?
QuentinUK
@QuentinUK Ihre beiden Ausdrücke sind beide gleich, x[y]definiert als*(x + (y))
MM
@ MM Ich dachte schon. Aber die andere Antwort von Arjun Sreedharan hat 38 Pfeile und dies hat -1. Und Arjun Sreedharans Antwort erwähnt kein undefiniertes Verhalten.
Antworten:
Zusammenfassung:
Vollständige Antwort:
Um die Größe Ihres Arrays in Bytes zu bestimmen, können Sie den
sizeof
Operator verwenden:Auf meinem Computer sind Ints 4 Bytes lang, also ist n 68.
Um die Anzahl der Elemente im Array zu bestimmen, können wir die Gesamtgröße des Arrays durch die Größe des Array-Elements teilen. Sie können dies mit dem Typ wie folgt tun:
und erhalten Sie die richtige Antwort (68/4 = 17), aber wenn sich die Art der
a
Änderung ändert, hätten Sie einen bösen Fehler, wenn Sie vergessen hätten, auch die zu ändernsizeof(int)
.Der bevorzugte Divisor ist also
sizeof(a[0])
oder das Äquivalentsizeof(*a)
die Größe des ersten Elements des Arrays.Ein weiterer Vorteil ist, dass Sie den Array-Namen jetzt einfach in einem Makro parametrisieren und erhalten können:
quelle
ARRAYSIZE
Makro definiertWinNT.h
(das von anderen Headern abgerufen wird ). WinAPI-Benutzer müssen also kein eigenes Makro definieren.static int a[20];
. Ihr Kommentar ist jedoch für Leser nützlich, die den Unterschied zwischen einem Array und einem Zeiger möglicherweise nicht erkennen.Der
sizeof
Weg ist der richtige, wenn Sie mit Arrays arbeiten, die nicht als Parameter empfangen wurden. Ein Array, das als Parameter an eine Funktion gesendet wird, wird als Zeiger behandelt, sodasssizeof
die Größe des Zeigers anstelle der Größe des Arrays zurückgegeben wird.Daher funktioniert diese Methode innerhalb von Funktionen nicht. Übergeben Sie stattdessen immer einen zusätzlichen Parameter
size_t size
, der die Anzahl der Elemente im Array angibt.Prüfung:
Ausgabe (in einem 64-Bit-Linux-Betriebssystem):
Ausgabe (in einem 32-Bit-Windows-Betriebssystem):
quelle
length of parameter:2
wenn nur ein Zeiger auf das 1. Array-Element übergeben wird?(sizeof array / sizeof *array)
.Es ist erwähnenswert, dass
sizeof
dies nicht hilfreich ist, wenn es sich um einen Array-Wert handelt, der in einen Zeiger verfallen ist: Obwohl er auf den Anfang eines Arrays zeigt, ist er für den Compiler dasselbe wie ein Zeiger auf ein einzelnes Element dieses Arrays . Ein Zeiger "erinnert" sich an nichts anderes über das Array, mit dem es initialisiert wurde.quelle
char
32 Bit hatte. Der Standard besagt lediglich, dass ganzzahlige Werte von 0 bis 127 dargestellt werden können und ihr Bereich mindestens entweder -127 bis 127 (Zeichen ist signiert) oder 0 bis 255 (Zeichen ist nicht signiert) beträgt.Die Größe des "Tricks" ist der beste Weg, den ich kenne, mit einer kleinen, aber (für mich ist dies ein großer Ärger mit Haustieren) wichtigen Änderung in der Verwendung von Klammern.
Wie der Wikipedia-Eintrag deutlich macht, ist C
sizeof
keine Funktion; Es ist ein Operator . Daher ist für das Argument keine Klammer erforderlich, es sei denn, das Argument ist ein Typname. Dies ist leicht zu merken, da das Argument dadurch wie ein Cast-Ausdruck aussieht, der auch Klammern verwendet.Also: Wenn Sie Folgendes haben:
Sie können die Anzahl der Elemente mit Code wie folgt finden:
Das liest sich für mich viel einfacher als die Alternative in Klammern. Ich bevorzuge auch die Verwendung des Sternchens im rechten Teil der Abteilung, da es prägnanter ist als die Indizierung.
Natürlich ist dies auch alles Kompilierungszeit, sodass Sie sich keine Sorgen über die Aufteilung machen müssen, die sich auf die Leistung des Programms auswirkt. Verwenden Sie dieses Formular also, wo immer Sie können.
Es ist immer am besten, sizeof für ein tatsächliches Objekt zu verwenden, wenn Sie eines haben, und nicht für einen Typ, da Sie sich dann nicht darum kümmern müssen, einen Fehler zu machen und den falschen Typ anzugeben.
Angenommen, Sie haben eine Funktion, die einige Daten als Bytestrom ausgibt, beispielsweise über ein Netzwerk. Rufen wir die Funktion auf
send()
und lassen sie als Argumente einen Zeiger auf das zu sendende Objekt und die Anzahl der Bytes im Objekt verwenden. So wird der Prototyp:Und dann müssen Sie eine Ganzzahl senden, damit Sie sie wie folgt codieren:
Jetzt haben Sie eine subtile Methode eingeführt, mit der Sie sich in den Fuß schießen können, indem Sie den Typ
foo
an zwei Stellen angeben. Wenn sich einer ändert, der andere jedoch nicht, wird der Code unterbrochen. Also mach es immer so:Jetzt bist du geschützt. Sicher, Sie duplizieren den Namen der Variablen, aber das hat eine hohe Wahrscheinlichkeit, dass der Compiler ihn erkennt, wenn Sie ihn ändern.
quelle
sizeof(int)
weniger Anweisungen alssizeof(foo)
?int x = 1+1;
versusint x = (1+1);
. Klammern sind hier rein ästhetisch.sizeof
wird in C ++ und C89 immer konstant sein. Mit den Arrays variabler Länge von C99 kann es zur Laufzeit ausgewertet werden.sizeof
kann ein Operator sein, sollte aber gemäß Linus Torvalds als Funktion behandelt werden. Genau. Lesen Sie seine Begründung hier: lkml.org/lkml/2012/7/11/103Schauen Sie sich diesen Link zur Erklärung an
quelle
ptrdiff_t
. (In der Regel ist dies auf einem 64-Bit-System ein größerer Typ alsint
). Auch wenn Sie ändern ,int
umptrdiff_t
in diesem Code, es hat immer noch einen Fehler , wenn inarr
Anspruch nimmt mehr als die Hälfte des Adressraumes./3G
Option Sie haben 3G / 1G Benutzer / Kernel Split, wodurch Sie Arrays Größe bis zu 75% der Adressraumgröße haben können.foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
als globale Variablen.buf3[]
Deklaration schlägt fehl, da(&buf1)[1] - buf1
es sich nicht um eine Konstante handelt.Sie können den Operator sizeof verwenden, er funktioniert jedoch nicht für Funktionen, da er die Referenz des Zeigers verwendet. Sie können Folgendes tun, um die Länge eines Arrays zu ermitteln:
Code, der ursprünglich hier gefunden wurde: C-Programm zum Ermitteln der Anzahl der Elemente in einem Array
quelle
Wenn Sie den Datentyp des Arrays kennen, können Sie Folgendes verwenden:
Wenn Sie den Datentyp des Arrays nicht kennen, können Sie Folgendes verwenden:
Hinweis: Diese Funktion funktioniert nur, wenn das Array zur Laufzeit nicht definiert ist (wie malloc) und das Array nicht in einer Funktion übergeben wird. In beiden Fällen ist
arr
(Array-Name) ein Zeiger.quelle
int noofele = sizeof(arr)/sizeof(int);
ist nur halbwegs besser als das Codierenint noofele = 9;
. Die Verwendungsizeof(arr)
behält die Flexibilität bei, falls sich die Arraygröße ändert. Benötigt jedochsizeof(int)
ein Update, sollte sich die Art derarr[]
Änderung ändern. Besser zu verwenden,sizeof(arr)/sizeof(arr[0])
auch wenn der Typ bekannt ist. Unklar, warumint
fürnoofele
vs. verwendet wirdsize_t
, der Typ, der von zurückgegeben wirdsizeof()
.Das Makro
ARRAYELEMENTCOUNT(x)
, das jeder verwendet, wird falsch ausgewertet . Dies ist realistisch gesehen nur eine heikle Angelegenheit, da Sie keine Ausdrücke haben können, die zu einem Array-Typ führen.Bewertet tatsächlich als:
Wohingegen
Es wird korrekt ausgewertet zu:
Dies hat wirklich nicht viel mit der expliziten Größe von Arrays zu tun. Ich habe gerade viele Fehler bemerkt, weil ich nicht wirklich beobachtet habe, wie der C-Präprozessor funktioniert. Sie schließen immer den Makroparameter ein, an dem möglicherweise kein Ausdruck beteiligt ist.
Das ist richtig; Mein Beispiel war schlecht. Aber genau das sollte passieren. Wie bereits erwähnt,
p + 1
wird dies als Zeigertyp enden und das gesamte Makro ungültig machen (genau wie wenn Sie versucht haben, das Makro in einer Funktion mit einem Zeigerparameter zu verwenden).Letztendlich spielt der Fehler in diesem speziellen Fall keine Rolle (also verschwende ich nur die Zeit aller; huzzah!), Weil Sie keine Ausdrücke mit einer Art 'Array' haben. Aber wirklich der Punkt über Subprozessor-Evaluierungs-Untertitel ist meiner Meinung nach wichtig.
quelle
(sizeof (x) / sizeof (*x))
?Für mehrdimensionale Arrays ist es etwas komplizierter. Oft definieren Menschen explizite Makrokonstanten, dh
Diese Konstanten können aber auch zur Kompilierungszeit mit sizeof ausgewertet werden :
Beachten Sie, dass dieser Code in C und C ++ funktioniert. Für Arrays mit mehr als zwei Dimensionen verwenden
usw. ad infinitum.
quelle
quelle
array
hat, die Sie nicht verwenden müssen ,sizeof(array) / sizeof(array[0])
wennarray
ein Array von entwederchar
,unsigned char
odersigned char
- Zitat aus C18,6.5.3.4 / 4: „Wenn sizeof auf einen Operanden angewandt wird, den Typ char, unsigned char hat, oder signed char , (oder eine qualifizierte Version davon) das Ergebnis ist 1. " In diesem Fall können Sie einfach das tunsizeof(array)
, was in meiner speziellen Antwort erläutert wurde .Größe eines Arrays in C:
quelle
size_t size_of_element
nochint
mitint n = sizeof (a) / sizeof (a[0]);
und nichtsize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
,int n
wie in verwendet,int n = sizeof (a) / sizeof (a[0]);
ist nicht ausreichend (es ist UB). Bei der Verwendungsize_t n = sizeof (a) / sizeof (a[0]);
tritt dieses Problem nicht auf.Ich würde raten, niemals
sizeof
eine der beiden unterschiedlichen Größen eines Arrays zu verwenden (auch wenn es verwendet werden kann), entweder in Anzahl der Elemente oder in Bytes. Dies sind die letzten beiden Fälle, die ich hier zeige. Für jede der beiden Größen können die unten gezeigten Makros verwendet werden, um die Sicherheit zu erhöhen. Der Grund ist offensichtlich die Absicht des Codes zu Maintainer zu machen, und die Differenzsizeof(ptr)
vonsizeof(arr)
auf dem ersten Blick (die auf diese Weise geschrieben ist nicht offensichtlich), so dass Fehler sind dann offensichtlich für jeden , den Code zu lesen.TL; DR:
must_be_array(arr)
(unten definiert) Wird benötigt, ebenso wie-Wsizeof-pointer-div
ein Buggy (ab April / 2020):Zu diesem Thema sind wichtige Fehler aufgetreten: https://lkml.org/lkml/2015/9/3/428
Ich bin nicht einverstanden mit der von Linus bereitgestellten Lösung, bei der niemals die Array-Notation für Parameter von Funktionen verwendet wird.
Ich mag die Array-Notation als Dokumentation, dass ein Zeiger als Array verwendet wird. Dies bedeutet jedoch, dass eine narrensichere Lösung angewendet werden muss, damit kein fehlerhafter Code geschrieben werden kann.
Aus einem Array haben wir drei Größen, die wir vielleicht wissen möchten:
Die Größe der Elemente des Arrays
Der erste ist sehr einfach und es spielt keine Rolle, ob es sich um ein Array oder einen Zeiger handelt, da dies auf die gleiche Weise erfolgt.
Anwendungsbeispiel:
qsort()
benötigt diesen Wert als drittes Argument.Für die beiden anderen Größen, die Gegenstand der Frage sind, möchten wir sicherstellen, dass es sich um ein Array handelt, und die Kompilierung unterbrechen, wenn nicht, da wir bei einem Zeiger falsche Werte erhalten . Wenn die Kompilierung fehlerhaft ist, können wir leicht erkennen, dass es sich nicht um ein Array, sondern um einen Zeiger handelte, und wir müssen den Code nur mit einer Variablen oder einem Makro schreiben, in dem die Größe des Arrays gespeichert ist Array hinter dem Zeiger.
Die Anzahl der Elemente im Array
Dies ist die häufigste und viele Antworten haben Ihnen das typische Makro ARRAY_SIZE geliefert:
Da das Ergebnis von ARRAY_SIZE häufig mit vorzeichenbehafteten Variablen vom Typ verwendet wird
ptrdiff_t
, empfiehlt es sich, eine vorzeichenbehaftete Variante dieses Makros zu definieren:Arrays mit mehr als
PTRDIFF_MAX
Mitgliedern geben ungültige Werte für diese signierte Version des Makros an, aber nach dem Lesen von C17 :: 6.5.6.9 spielen solche Arrays bereits mit dem Feuer. NurARRAY_SIZE
undsize_t
sollte in diesen Fällen verwendet werden.Neuere Versionen von Compilern wie GCC 8 warnen Sie, wenn Sie dieses Makro auf einen Zeiger anwenden, damit es sicher ist (es gibt andere Methoden, um es mit älteren Compilern sicher zu machen).
Es funktioniert, indem die Größe des gesamten Arrays in Bytes durch die Größe jedes Elements geteilt wird.
Anwendungsbeispiele:
Wenn diese Funktionen keine Arrays verwenden, sondern sie stattdessen als Parameter erhalten würden, würde der frühere Code nicht kompiliert, sodass es unmöglich wäre, einen Fehler zu haben (vorausgesetzt, dass eine neuere Compilerversion verwendet wird oder ein anderer Trick verwendet wird). und wir müssen den Makroaufruf durch den Wert ersetzen:
Die Größe in Bytes, die das Array im Speicher verwendet
ARRAY_SIZE
wird häufig als Lösung für den vorherigen Fall verwendet, aber dieser Fall wird selten sicher geschrieben, möglicherweise weil er weniger häufig ist.Der übliche Weg, um diesen Wert zu erhalten, ist die Verwendung
sizeof(arr)
. Das Problem: das gleiche wie beim vorherigen; Wenn Sie einen Zeiger anstelle eines Arrays haben, wird Ihr Programm verrückt.Die Lösung des Problems besteht darin, dasselbe Makro wie zuvor zu verwenden, von dem wir wissen, dass es sicher ist (es bricht die Kompilierung ab, wenn es auf einen Zeiger angewendet wird):
Wie es funktioniert, ist sehr einfach: Es macht die Teilung rückgängig, die dies
ARRAY_SIZE
tut. Nach mathematischen Stornierungen erhalten Sie also nur einesizeof(arr)
, aber mit der zusätzlichen Sicherheit derARRAY_SIZE
Konstruktion.Anwendungsbeispiel:
memset()
benötigt diesen Wert als drittes Argument.Wenn das Array als Parameter (Zeiger) empfangen wird, wird es wie zuvor nicht kompiliert, und wir müssen den Makroaufruf durch den Wert ersetzen:
Update (23. April 2020):
-Wsizeof-pointer-div
ist fehlerhaft :Heute habe ich herausgefunden, dass die neue Warnung in GCC nur funktioniert, wenn das Makro in einem Header definiert ist, der kein Systemheader ist. Wenn Sie das Makro in einem Header definieren, der in Ihrem System installiert ist (normalerweise
/usr/local/include/
oder/usr/include/
) (#include <foo.h>
), gibt der Compiler KEINE Warnung aus (ich habe GCC 9.3.0 ausprobiert).Also haben
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
und wollen wir es sicher machen. Wir benötigen C11_Static_assert()
und einige GCC-Erweiterungen: Anweisungen und Deklarationen in Ausdrücken , __builtin_types_compatible_p :Jetzt
ARRAY_SIZE()
ist es völlig sicher, und daher sind alle seine Derivate sicher.Update: libbsd bietet
__arraycount()
:Libbsd stellt das Makro
__arraycount()
in bereit<sys/cdefs.h>
, was unsicher ist, da es keine Klammern enthält, aber wir können diese Klammern selbst hinzufügen, und daher müssen wir nicht einmal die Unterteilung in unseren Header schreiben (warum sollten wir bereits vorhandenen Code duplizieren? ). Dieses Makro ist in einem Systemheader definiert. Wenn wir es also verwenden, müssen wir die obigen Makros verwenden.Einige Systeme bieten
nitems()
in<sys/param.h>
statt und einige Systeme bieten beides. Sie sollten Ihr System überprüfen und das System verwenden, das Sie haben, und möglicherweise einige Präprozessorbedingungen für die Portabilität verwenden und beide unterstützen.Update: Ermöglicht die Verwendung des Makros im Dateibereich:
Leider kann die
({})
Erweiterung gcc im Dateibereich nicht verwendet werden. Um das Makro im Dateibereich verwenden zu können, muss sich die statische Zusicherung darin befindensizeof(struct {})
. Multiplizieren Sie es dann mit0
, um das Ergebnis nicht zu beeinflussen. Eine Umwandlung in(int)
kann gut sein, um eine zurückgegebene Funktion zu simulieren(int)0
(in diesem Fall ist sie nicht erforderlich, kann dann aber für andere Zwecke wiederverwendet werden).quelle
sizeof(arr)
), die an keiner anderen Stelle gezeigt wird :ARRAY_BYTES(arr)
.sizeof
, sondern stattdessen diese Konstruktionen zu verwenden; Wenn Sie diese Konstruktionen jedes Mal schreibensizeof
ist eindeutig unsicher (Gründe sind in der Antwort enthalten), und nicht Makros zu verwenden, sondern die von mir bereitgestellten Konstruktionen jedes Mal zu verwenden, ist noch unsicherer, also der einzige Weg ist Makros."Sie haben eine subtile Art eingeführt, sich in den Fuß zu schießen."
C 'native' Arrays speichern ihre Größe nicht. Es wird daher empfohlen, die Länge des Arrays in einer separaten Variablen / const zu speichern und sie immer dann zu übergeben, wenn Sie das Array übergeben, d. H.
Sie sollten native Arrays immer vermeiden (es sei denn, Sie können sich in diesem Fall nicht um Ihren Fuß kümmern). Wenn Sie C ++ schreiben, verwenden Sie den 'Vektor'-Container der STL . "Im Vergleich zu Arrays bieten sie fast die gleiche Leistung" und sind weitaus nützlicher!
Siehe: http://www.cplusplus.com/reference/stl/vector/
quelle
enum
Deklaration zu verwenden.quelle
Wenn Sie dies wirklich tun möchten, um Ihr Array zu umgehen, empfehle ich, eine Struktur zu implementieren, um einen Zeiger auf den Typ zu speichern, von dem Sie ein Array und eine Ganzzahl, die die Größe des Arrays darstellt, möchten. Dann können Sie das an Ihre Funktionen weitergeben. Weisen Sie diesem Zeiger einfach den Wert der Array-Variablen (Zeiger auf das erste Element) zu. Dann können Sie
Array.arr[i]
das i-te Element abrufen undArray.size
die Anzahl der Elemente im Array ermitteln.Ich habe einen Code für Sie eingefügt. Es ist nicht sehr nützlich, aber Sie könnten es um weitere Funktionen erweitern. Um ehrlich zu sein, sollten Sie, wenn dies die gewünschten Dinge sind, die Verwendung von C beenden und eine andere Sprache mit diesen integrierten Funktionen verwenden.
quelle
strcpy(d.name, name);
ohne Behandlung des Überlaufs funktioniert, kann nicht hochgestuft werden .Am besten speichern Sie diese Informationen beispielsweise in einer Struktur:
Implementieren Sie alle erforderlichen Funktionen wie Erstellen, Zerstören, Überprüfen der Gleichheit und alles andere, was Sie benötigen. Es ist einfacher, als Parameter zu übergeben.
quelle
int elements
vs.size_t elements
?Die Funktion
sizeof
gibt die Anzahl der Bytes zurück, die von Ihrem Array im Speicher verwendet werden. Wenn Sie die Anzahl der Elemente in Ihrem Array berechnen möchten, sollten Sie diese Anzahl durch densizeof
Variablentyp des Arrays teilen . Angenommenint array[10];
, wenn die Ganzzahl vom Variablentyp in Ihrem Computer 32 Bit (oder 4 Byte) beträgt, sollten Sie Folgendes tun, um die Größe Ihres Arrays zu ermitteln:quelle
Sie können den
&
Operator verwenden. Hier ist der Quellcode:Hier ist die Beispielausgabe
quelle
ptrdiff_t
.sizeof()
führt zusize_t
. C definiert nicht , welcher breiter oder höher / gleicher Rang ist. Die Art des Quotienten((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
ist also nicht sichersize_t
und daher kann das Drucken mitz
zu UB führen. Einfach zu benutzenprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
ist ausreichend.(char *)(&a+1)-(char *)a
ist keine Konstante und kann zur Laufzeit auch bei fester Größe berechnet werdena[10]
.sizeof(a)/sizeof(a[0])
wird in diesem Fall konstant zur Kompilierungszeit ausgeführt.Die einfachste Antwort:
quelle
Eine elegantere Lösung wird sein
quelle
Neben den bereits gegebenen Antworten möchte ich auf einen Sonderfall durch die Verwendung von hinweisen
Wenn
a
es sich entweder um ein Array vonchar
handeltunsigned char
odersigned char
Sie es nichtsizeof
zweimalsizeof
verwenden müssen, ergibt sich immer ein Ausdruck mit einem Operanden dieser Typen1
.Zitat aus C18,6.5.3.4 / 4:
Somit
sizeof(a) / sizeof (a[0])
wäre äquivalent ,NUMBER OF ARRAY ELEMENTS / 1
wenna
ein Array des Typschar
,unsigned char
odersigned char
. Die Division durch 1 ist redundant.In diesem Fall können Sie einfach abkürzen und Folgendes tun:
Zum Beispiel:
Wenn Sie einen Beweis wollen, finden Sie hier einen Link zu GodBolt .
Trotzdem behält die Abteilung die Sicherheit bei, wenn sich der Typ erheblich ändert (obwohl diese Fälle selten sind).
quelle
Hinweis: Dieser kann Ihnen undefiniertes Verhalten geben, wie von MM im Kommentar angegeben.
Weitere Details finden Sie hier und auch hier .
quelle
*
Operator darf nicht auf einen Past-the-End-Zeiger angewendet werden*(&a+1) - a;
ist anders als(&a)[1] - a;
oben, nicht beide*(&a+1)
und(&a)[1]
zählen als 1 nach dem Ende?x[y]
definiert als*(x + (y))