Wie bestimme ich die Größe meines Arrays in C?
Das heißt, die Anzahl der Elemente, die das Array enthalten kann?
Wie bestimme ich die Größe meines Arrays in C?
Das heißt, die Anzahl der Elemente, die das Array enthalten kann?
Antworten:
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);
ARRAYSIZE
Makro definiert WinNT.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, sodass sizeof
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:
#include <stdio.h>
#include <stdlib.h>
void printSizeOf(int intArray[]);
void printLength(int intArray[]);
int main(int argc, char* argv[])
{
int array[] = { 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: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
Ausgabe (in einem 32-Bit-Windows-Betriebssystem):
sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
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.
int a[10];
int* p = a;
assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
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:
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(const void *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 foo
an 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.
sizeof(int)
weniger Anweisungen als sizeof(foo)
?
int x = 1+1;
versus int 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/103
int size = (&arr)[1] - arr;
Schauen Sie sich diesen Link zur Erklärung an
ptrdiff_t
. (In der Regel ist dies auf einem 64-Bit-System ein größerer Typ als int
). Auch wenn Sie ändern , int
um ptrdiff_t
in diesem Code, es hat immer noch einen Fehler , wenn in arr
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:
len = sizeof(arr)/sizeof(arr[0])
Code, der ursprünglich hier gefunden wurde: C-Programm zum Ermitteln der Anzahl der Elemente in einem Array
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 int
für noofele
vs. verwendet wird size_t
, der Typ, der von zurückgegeben wird sizeof()
.
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.
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))
ARRAYELEMENTCOUNT(p + 1);
Bewertet tatsächlich als:
(sizeof (p + 1) / sizeof (p + 1[0]));
Wohingegen
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])
ARRAYELEMENTCOUNT(p + 1);
Es wird korrekt ausgewertet zu:
(sizeof (p + 1) / sizeof (p + 1)[0]);
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.
(sizeof (x) / sizeof (*x))
?
Für mehrdimensionale Arrays ist es etwas komplizierter. Oft definieren Menschen explizite Makrokonstanten, dh
#define g_rgDialogRows 2
#define g_rgDialogCols 7
static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
{ " ", " ", " ", " 494", " 210", " Generic Sample Dialog", " " },
{ " 1", " 330", " 174", " 88", " ", " OK", " " },
};
Diese Konstanten können aber auch zur Kompilierungszeit mit sizeof ausgewertet werden :
#define rows_of_array(name) \
(sizeof(name ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name) \
(sizeof(name[0]) / sizeof(name[0][0]))
static char* g_rgDialog[][7] = { /* ... */ };
assert( rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);
Beachten Sie, dass dieser Code in C und C ++ funktioniert. Für Arrays mit mehr als zwei Dimensionen verwenden
sizeof(name[0][0][0])
sizeof(name[0][0][0][0])
usw. ad infinitum.
sizeof(array) / sizeof(array[0])
array
hat, die Sie nicht verwenden müssen , sizeof(array) / sizeof(array[0])
wenn array
ein Array von entweder char
, unsigned char
oder 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 .
Größe eines Arrays in C:
int a[10];
size_t size_of_array = sizeof(a); // Size of array a
int n = sizeof (a) / sizeof (a[0]); // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a
// Size of each element = size of type
size_t size_of_element
noch int
mit int 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 Verwendung size_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 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.
TL; DR:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
must_be_array(arr)
(unten definiert) Wird benötigt, ebenso wie -Wsizeof-pointer-div
ein Buggy (ab April / 2020):
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a) ( \
0 * (int)sizeof( \
struct { \
Static_assert_array(a); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
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:
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:
void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
qsort(arr, nmemb, sizeof(arr[0]), cmp);
}
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.
Dies ist die häufigste und viele Antworten haben Ihnen das typische Makro ARRAY_SIZE geliefert:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
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:
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
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. Nur ARRAY_SIZE
und size_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:
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;
}
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):
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
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 eine sizeof(arr)
, aber mit der zusätzlichen Sicherheit der ARRAY_SIZE
Konstruktion.
Anwendungsbeispiel:
void foo(ptrdiff_t nmemb)
{
int arr[nmemb];
memset(arr, 0, ARRAY_BYTES(arr));
}
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:
void foo(ptrdiff_t nmemb, int arr[nmemb])
{
memset(arr, 0, sizeof(arr[0]) * nmemb);
}
-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 :
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_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.
__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.
#include <sys/cdefs.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define ARRAY_SIZE(arr) ( \
{ \
Static_assert_array(arr); \
__arraycount((arr)); \
} \
)
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
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.
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).
#include <sys/cdefs.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a) ( \
0 * (int)sizeof( \
struct { \
Static_assert_array(a); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
#define ARRAY_SIZE(arr) (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
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 schreiben
sizeof
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.
#define MY_ARRAY_LENGTH 15
int myArray[MY_ARRAY_LENGTH];
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 ints
vector<int> numbers;
// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
numbers.push_back(i);
// Determine the size of the array
cout << numbers.size();
enum
Deklaration zu verwenden.
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
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.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.
/* 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
*/
typedef struct MyType
{
int age;
char name[20];
} MyType;
typedef struct MyTypeArray
{
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. */
MyTypeArray array = new_MyTypeArray(2, arr);
/* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
print_MyType_names(array);
return 0;
}
strcpy(d.name, name);
ohne Behandlung des Überlaufs funktioniert, kann nicht hochgestuft werden .
Am besten speichern Sie diese Informationen beispielsweise in einer Struktur:
typedef struct {
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.
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 den sizeof
Variablentyp 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:
int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
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])));
return 0;
};
Hier ist die Beispielausgabe
1549216672
1549216712
---- diff----
4
The size of array a is 10
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_t
und daher kann das Drucken mit z
zu UB führen. Einfach zu benutzen printf("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 werden a[10]
. sizeof(a)/sizeof(a[0])
wird in diesem Fall konstant zur Kompilierungszeit ausgeführt.
Eine elegantere Lösung wird sein
size_t size = sizeof(a) / sizeof(*a);
Neben den bereits gegebenen Antworten möchte ich auf einen Sonderfall durch die Verwendung von hinweisen
sizeof(a) / sizeof (a[0])
Wenn a
es sich entweder um ein Array von char
handelt unsigned char
oder signed char
Sie es nicht sizeof
zweimal sizeof
verwenden müssen, ergibt sich immer ein Ausdruck mit einem Operanden dieser Typen 1
.
Zitat aus C18,6.5.3.4 / 4:
„ Wenn
sizeof
auf einen Operanden angewandt , die Aktivität hatchar
,unsigned char
odersigned char
, (oder eine qualifizierte Version davon) das Ergebnis ist1
.“
Somit sizeof(a) / sizeof (a[0])
wäre äquivalent , NUMBER OF ARRAY ELEMENTS / 1
wenn a
ein Array des Typs char
, unsigned char
oder 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).
Hinweis: Dieser kann Ihnen undefiniertes Verhalten geben, wie von MM im Kommentar angegeben.
int a[10];
int size = (*(&a+1)-a) ;
*
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))