Félicitations pour avoir atteint 1000 votes positifs!
SS Anne
Réponses:
1262
Résumé:
int a[17];size_t n =sizeof(a)/sizeof(a[0]);
Réponse complète:
Pour déterminer la taille de votre tableau en octets, vous pouvez utiliser l' sizeof
opérateur:
int a[17];size_t n =sizeof(a);
Sur mon ordinateur, les entiers font 4 octets, donc n est 68.
Pour déterminer le nombre d'éléments dans le tableau, nous pouvons diviser la taille totale du tableau par la taille de l'élément du tableau. Vous pouvez le faire avec le type, comme ceci:
int a[17];size_t n =sizeof(a)/sizeof(int);
et obtenez la bonne réponse (68/4 = 17), mais si le type de
achangement vous aurait un méchant bug si vous oubliez de le changer sizeof(int)aussi.
Ainsi, le diviseur préféré est sizeof(a[0])ou l'équivalent sizeof(*a), la taille du premier élément du tableau.
int a[17];size_t n =sizeof(a)/sizeof(a[0]);
Un autre avantage est que vous pouvez désormais facilement paramétrer le nom du tableau dans une macro et obtenir:
#define NELEMS(x)(sizeof(x)/sizeof((x)[0]))int a[17];size_t n = NELEMS(a);
Le code généré sera identique, car le compilateur connaît le type de * int_arr au moment de la compilation (et donc la valeur de sizeof (* int_arr)). Ce sera une constante, et le compilateur peut optimiser en conséquence.
Mark Harrison
10
Cela devrait être le cas avec tous les compilateurs, car les résultats de sizeof sont définis comme une constante de temps de compilation.
Mark Harrison
451
Important : n'arrêtez pas de lire ici, lisez la réponse suivante! Cela ne fonctionne que pour les tableaux de la pile , par exemple si vous utilisez malloc () ou accédez à un paramètre de fonction, vous n'avez pas de chance. Voir ci-dessous.
Markus
7
Pour la programmation de l'API Windows en C ou C ++, il y a le ARRAYSIZEmakro défini dans WinNT.h(qui est tiré par d'autres en-têtes). Les utilisateurs de WinAPI n'ont donc pas besoin de définir leur propre makro.
Lumi
17
@Markus cela fonctionne pour n'importe quelle variable qui a un type de tableau; cela ne doit pas être "sur la pile". Par exemple static int a[20];. Mais votre commentaire est utile aux lecteurs qui ne réalisent pas la différence entre un tableau et un pointeur.
MM
808
La sizeofmanière est la bonne, si vous traitez des tableaux non reçus comme paramètres. Un tableau envoyé en tant que paramètre à une fonction est traité comme un pointeur, il sizeofretournera donc la taille du pointeur au lieu de celle du tableau.
Ainsi, à l'intérieur des fonctions, cette méthode ne fonctionne pas. Au lieu de cela, passez toujours un paramètre supplémentaire size_t sizeindiquant le nombre d'éléments dans le tableau.
Tester:
#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])));}
Sortie (dans un système d'exploitation Linux 64 bits):
sizeof of array:28sizeof of parameter:8Length of array:7Length of parameter:2
Sortie (dans un système d'exploitation Windows 32 bits):
sizeof of array:28sizeof of parameter:4Length of array:7Length of parameter:1
pourquoi length of parameter:2si seulement un pointeur vers le 1er élément du tableau est passé?
Bbvarghe
16
@Bbvarghe C'est parce que les pointeurs dans les systèmes 64 bits font 8 octets (sizeof (intArray)), mais les entiers ont toujours (généralement) 4 octets de long (sizeof (intArray [0])).
Elideb
13
@Pacerier: Il n'y a pas de code correct - la solution habituelle est de passer la longueur avec le tableau comme argument séparé.
Jean Hominal
10
Attendez, donc il n'y a aucun moyen d'accéder directement au tableau à partir d'un pointeur et de voir sa taille? Nouveau sur C ici.
sudo
7
Trouw @ Michael: vous pouvez utiliser la syntaxe de l' opérateur si est fait vous sentir mieux: (sizeof array / sizeof *array).
chqrlie du
134
Il convient de noter que cela sizeofn'aide pas lorsqu'il s'agit d'une valeur de tableau qui s'est désintégrée en un pointeur: même si elle pointe vers le début d'un tableau, pour le compilateur, c'est la même chose qu'un pointeur vers un seul élément de ce tableau . Un pointeur ne "se souvient" de rien d'autre du tableau qui a été utilisé pour l'initialiser.
int a[10];int* p = a;
assert(sizeof(a)/sizeof(a[0])==10);
assert(sizeof(p)==sizeof(int*));
assert(sizeof(*p)==sizeof(int));
@ Magnus: La norme définit sizeof comme donnant le nombre d'octets dans l'objet et que sizeof (char) est toujours un. Le nombre de bits dans un octet est spécifique à l'implémentation. Edit: ANSI C ++ standard section 5.3.3 Sizeof: "L'opérateur sizeof donne le nombre d'octets dans la représentation d'objet de son opérande. [...] sizeof (char), sizeof (char signé) et sizeof (char non signé) sont 1; le résultat de sizeof appliqué à tout autre type fondamental est défini par l'implémentation. "
Skizz
Section 1.6 Le modèle de mémoire C ++: "L'unité de stockage fondamentale dans le modèle de mémoire C ++ est l'octet. Un octet est au moins assez grand pour contenir n'importe quel membre du jeu de caractères d'exécution de base et est composé d'une séquence contiguë de bits, le nombre dont la mise en œuvre est définie. "
Skizz
2
Je me souviens que le CRAY avait C avec char32 bits. Tout ce que la norme dit, c'est que les valeurs entières de 0 à 127 peuvent être représentées, et sa plage est au moins de -127 à 127 (le caractère est signé) ou de 0 à 255 (le caractère est non signé).
vonbrand
5
C'est une excellente réponse. Je tiens à dire que toutes les affirmations ci-dessus sont évaluées à VRAI.
Javad
49
La taille du "truc" est le meilleur moyen que je connaisse, avec un petit mais (pour moi, ce qui est une bête noire majeure) un changement important dans l'utilisation des parenthèses.
Comme l'indique clairement l'entrée Wikipedia, les C ne sizeofsont pas une fonction; c'est un opérateur . Ainsi, il ne nécessite pas de parenthèses autour de son argument, sauf si l'argument est un nom de type. Ceci est facile à retenir, car il fait ressembler l'argument à une expression cast, qui utilise également des parenthèses.
Donc: si vous avez les éléments suivants:
int myArray[10];
Vous pouvez trouver le nombre d'éléments avec du code comme celui-ci:
size_t n =sizeof myArray /sizeof*myArray;
Pour moi, cela se lit beaucoup plus facilement que l'alternative avec parenthèses. Je suis également favorable à l'utilisation de l'astérisque dans la partie droite de la division, car il est plus concis que l'indexation.
Bien sûr, tout cela est également au moment de la compilation, il n'est donc pas nécessaire de s'inquiéter de la division affectant les performances du programme. Utilisez donc ce formulaire partout où vous le pouvez.
Il est toujours préférable d'utiliser sizeof sur un objet réel lorsque vous en avez un, plutôt que sur un type, car vous n'avez alors pas à vous soucier de faire une erreur et d'indiquer le mauvais type.
Par exemple, supposons que vous ayez une fonction qui génère des données sous forme de flux d'octets, par exemple sur un réseau. Appelons la fonction send()et faisons en sorte qu'elle prenne comme arguments un pointeur vers l'objet à envoyer et le nombre d'octets dans l'objet. Ainsi, le prototype devient:
void send(constvoid*object,size_t size);
Et puis vous devez envoyer un entier, donc vous le codez comme ceci:
int foo =4711;
send(&foo,sizeof(int));
Maintenant, vous avez introduit une manière subtile de vous tirer une balle dans le pied, en spécifiant le type de fooà deux endroits. Si l'un change mais pas l'autre, le code se casse. Ainsi, faites-le toujours comme ceci:
send(&foo,sizeof foo);
Maintenant tu es protégé. Bien sûr, vous dupliquez le nom de la variable, mais cela a une forte probabilité de se casser d'une manière que le compilateur peut détecter, si vous la changez.
Au fait, sont-ils des instructions identiques au niveau du processeur? Nécessite sizeof(int)moins d'instructions que sizeof(foo)?
Pacerier
@Pacerier: non, ils sont identiques. Pensez à int x = 1+1;versus int x = (1+1);. Ici, les parenthèses sont purement absolument juste esthétiques.
quetzalcoatl
@Aidiakapi Ce n'est pas vrai, considérez les VLA C99.
détendre le
@unwind Merci, je me tiens corrigé. Pour corriger mon commentaire, sizeofsera toujours constant en C ++ et C89. Avec les tableaux à longueur variable de C99, il peut être évalué au moment de l'exécution.
Aidiakapi
2
sizeofpeut être un opérateur mais il doit être traité comme une fonction selon Linus Torvalds. Je suis d'accord. Lisez son rationnel ici: lkml.org/lkml/2012/7/11/103
Petite piqûre: le résultat de la soustraction du pointeur est de type ptrdiff_t. (Généralement sur un système 64 bits, ce sera un type plus grand que int). Même si vous passez intà ptrdiff_tdans ce code, il a toujours un bogue s'il arroccupe plus de la moitié de l'espace d'adressage.
MM
2
@MM Un autre petit problème: en fonction de l'architecture de votre système, l'espace d'adressage n'est pas aussi grand que la taille du pointeur sur la plupart des systèmes. Windows, par exemple, limite l'espace d'adressage pour les applications 64 bits à 8 To ou 44 bits. Ainsi, même si vous avez un tableau supérieur à la moitié de votre espace d'adressage de 4,1 To par exemple, ce ne sera pas un bug. Seulement si votre espace d'adressage dépasse 63 bits sur ces systèmes, il est même possible de rencontrer un tel bogue. En général, ne vous en faites pas.
Aidiakapi du
1
@Aidiakapi sur Linux x86 32 bits ou sur Windows avec l' /3Goption vous avez un partage utilisateur / noyau 3G / 1G, ce qui vous permet d'avoir une taille de tableau pouvant atteindre 75% de la taille de l'espace d'adressage.
Ruslan
1
Considérez foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];comme variables globales. buf3[]échoue car ce (&buf1)[1] - buf1n'est pas une constante.
chux
2
Il s'agit d'un comportement techniquement indéfini car la norme interdit explicitement le déréférencement au-delà de la fin d'un tableau (même si vous n'essayez pas de lire la valeur stockée)
MM
26
Vous pouvez utiliser l'opérateur sizeof mais cela ne fonctionnera pas pour les fonctions car il prendra la référence du pointeur, vous pouvez faire ce qui suit pour trouver la longueur d'un tableau:
Si vous connaissez le type de données du tableau, vous pouvez utiliser quelque chose comme:
int arr[]={23,12,423,43,21,43,65,76,22};int noofele =sizeof(arr)/sizeof(int);
Ou si vous ne connaissez pas le type de données du tableau, vous pouvez utiliser quelque chose comme:
noofele =sizeof(arr)/sizeof(arr[0]);
Remarque: Cette chose ne fonctionne que si le tableau n'est pas défini au moment de l'exécution (comme malloc) et que le tableau n'est pas passé dans une fonction. Dans les deux cas, arr(nom du tableau) est un pointeur.
int noofele = sizeof(arr)/sizeof(int);n'est qu'à mi-chemin meilleur que le codage int noofele = 9;. L'utilisation sizeof(arr)maintient la flexibilité en cas de modification de la taille du tableau. Encore sizeof(int)faut-il une mise à jour en cas de arr[]changement de type . Mieux vaut l'utiliser sizeof(arr)/sizeof(arr[0])même si le type est bien connu. On ne sait pas pourquoi utiliser intpour noofelevs size_t, le type renvoyé par sizeof().
chux
19
La macro ARRAYELEMENTCOUNT(x)que tout le monde utilise n'évalue pas correctement . En réalité, ceci est juste une question sensible, car vous ne pouvez pas avoir d'expressions qui aboutissent à un type «tableau».
Cela n'a vraiment pas grand-chose à voir avec la taille des tableaux explicitement. Je viens de remarquer beaucoup d'erreurs de ne pas vraiment observer le fonctionnement du préprocesseur C. Vous encapsulez toujours le paramètre de macro dans lequel aucune expression ne peut être impliquée.
C'est correct; mon exemple était mauvais. Mais c'est exactement ce qui devrait arriver. Comme je l'ai mentionné précédemment, cela p + 1se terminera comme un type de pointeur et invalidera la macro entière (comme si vous tentiez d'utiliser la macro dans une fonction avec un paramètre de pointeur).
À la fin de la journée, dans ce cas particulier , la faute n'a pas vraiment d'importance (donc je perds juste le temps de tout le monde; huzzah!), Parce que vous n'avez pas d'expressions avec un type de 'tableau'. Mais vraiment, le point sur les subtils d'évaluation du préprocesseur est, je pense, important.
Merci pour l'explication. La version d'origine entraîne une erreur de compilation. Clang rapporte que "la valeur en indice n'est pas un tableau, un pointeur ou un vecteur". Cela semble préférable dans ce cas, bien que vos commentaires sur l'ordre d'évaluation dans les macros soient bien pris en compte.
Mark Harrison
1
Je n'avais pas pensé à la plainte du compilateur comme une notification automatique d'un type incorrect. Merci!
3
Y a-t-il une raison de ne pas l'utiliser (sizeof (x) / sizeof (*x))?
seriousdev
16
Pour les tableaux multidimensionnels, c'est un peu plus compliqué. Souvent, les gens définissent des constantes macro explicites, c.-à-d.
En fonction du type arraya, vous n'avez pas besoin d'utiliser sizeof(array) / sizeof(array[0])si arrayest un tableau de deux char, unsigned charou signed char- Citation de C18,6.5.3.4 / 4: « Lorsque sizeof est appliqué à un opérande qui est de type char, unsigned char, ou char signé , (ou une version qualifiée de celui-ci) le résultat est 1. " Dans ce cas, vous pouvez simplement faire sizeof(array)comme expliqué dans ma réponse dédiée .
RobertS soutient Monica Cellio
15
Taille d'un tableau en 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
Curieux que le code soit size_t size_of_elementencore utilisé intavec int n = sizeof (a) / sizeof (a[0]); et nonsize_t n = sizeof (a) / sizeof (a[0]);
chux - Réintègre Monica
1
Salut @Yogeesh HT, pouvez-vous s'il vous plaît répondre au doute de chux. Je suis également très curieux de savoir comment int n = sizeof (a) / sizeof (a [0]) donne la longueur du tableau et pourquoi nous n'utilisons pas size_t pour la longueur du tableau. Quelqu'un peut-il y répondre?
Brain
1
@Brain sizeof (a) donne la taille de tous les éléments présents dans le tableau une tailleof (a [0]) donne la taille du 1er élément. Supposons a = {1,2,3,4,5}; sizeof (a) = 20 octets (si sizeof (int) = 4 octets multiplier 5), sizeof (a [0]) = 4 octets, donc 20/4 = 5, c'est-à-dire aucun élément
Yogeesh HT
2
@YogeeshHT Pour les très grands tableaux comme char a[INT_MAX + 1u];, int ntel qu'utilisé dans int n = sizeof (a) / sizeof (a[0]);est insuffisant (c'est UB). L'utilisation size_t n = sizeof (a) / sizeof (a[0]);n'entraîne pas ce problème.
chux
13
Je conseillerais de ne jamais utiliser sizeof(même s'il peut être utilisé) pour obtenir l'une des deux tailles différentes d'un tableau, soit en nombre d'éléments soit en octets, qui sont les deux derniers cas que je montre ici. Pour chacune des deux tailles, les macros illustrées ci-dessous peuvent être utilisées pour la rendre plus sûre. La raison est de rendre évidente l'intention du code à mainteneurs, et la différence sizeof(ptr)de sizeof(arr)prime abord (qui écrit de cette façon est pas évident), de sorte que les insectes sont alors évident pour tout le monde à lire le code.
Je ne suis pas d'accord avec la solution proposée par Linus, qui consiste à ne jamais utiliser la notation matricielle pour les paramètres des fonctions.
J'aime la notation de tableau comme documentation qu'un pointeur est utilisé comme tableau. Mais cela signifie qu'une solution infaillible doit être appliquée pour qu'il soit impossible d'écrire du code bogué.
À partir d'un tableau, nous avons trois tailles que nous pourrions vouloir savoir:
La taille des éléments du tableau
Le nombre d'éléments dans le tableau
La taille en octets que le tableau utilise en mémoire
La taille des éléments du tableau
Le premier est très simple, et cela n'a pas d'importance si nous avons affaire à un tableau ou à un pointeur, car c'est fait de la même manière.
qsort() a besoin de cette valeur comme troisième argument.
Pour les deux autres tailles, qui sont le sujet de la question, nous voulons nous assurer que nous avons affaire à un tableau, et casser la compilation sinon, car si nous avons affaire à un pointeur, nous obtiendrons des valeurs erronées . Lorsque la compilation est interrompue, nous pourrons facilement voir que nous n'avons pas affaire à un tableau, mais à un pointeur à la place, et nous devrons simplement écrire le code avec une variable ou une macro qui stocke la taille du tableau derrière le pointeur.
Le nombre d'éléments dans le tableau
Celui-ci est le plus courant, et de nombreuses réponses vous ont fourni la macro typique ARRAY_SIZE:
Étant donné que le résultat de ARRAY_SIZE est couramment utilisé avec des variables de type signées ptrdiff_t, il est bon de définir une variante signée de cette macro:
Les tableaux avec plus de PTRDIFF_MAXmembres vont donner des valeurs invalides pour cette version signée de la macro, mais à la lecture de C17 :: 6.5.6.9, des tableaux comme celui-ci jouent déjà avec le feu. Uniquement ARRAY_SIZEet size_tdoit être utilisé dans ces cas.
Les versions récentes de compilateurs, tels que GCC 8, vous avertiront lorsque vous appliquerez cette macro à un pointeur, elle est donc sûre (il existe d'autres méthodes pour la sécuriser avec des compilateurs plus anciens).
Il fonctionne en divisant la taille en octets de l'ensemble du tableau par la taille de chaque élément.
Exemples d'utilisation:
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;}
Si ces fonctions n'utilisaient pas de tableaux, mais les obtenaient à la place comme paramètres, l'ancien code ne se compilerait pas, il serait donc impossible d'avoir un bogue (étant donné qu'une version récente du compilateur est utilisée, ou qu'une autre astuce est utilisée) , et nous devons remplacer l'appel de macro par la valeur:
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;}
La taille en octets que le tableau utilise en mémoire
ARRAY_SIZE est couramment utilisé comme solution au cas précédent, mais ce cas est rarement écrit en toute sécurité, peut-être parce qu'il est moins courant.
La manière courante d'obtenir cette valeur est d'utiliser sizeof(arr). Le problème: le même qu'avec le précédent; si vous avez un pointeur au lieu d'un tableau, votre programme deviendra fou.
La solution au problème consiste à utiliser la même macro que précédemment, que nous savons être sûre (elle rompt la compilation si elle est appliquée à un pointeur):
Son fonctionnement est très simple: il annule la division qui le ARRAY_SIZEfait, donc après des annulations mathématiques, vous vous retrouvez avec une seule sizeof(arr), mais avec la sécurité supplémentaire de la ARRAY_SIZEconstruction.
memset() a besoin de cette valeur comme troisième argument.
Comme précédemment, si le tableau est reçu en paramètre (un pointeur), il ne se compilera pas, et nous devrons remplacer l'appel de macro par la valeur:
Aujourd'hui, j'ai découvert que le nouvel avertissement dans GCC ne fonctionne que si la macro est définie dans un en-tête qui n'est pas un en-tête système. Si vous définissez la macro dans un en-tête installé sur votre système (généralement /usr/local/include/ou /usr/include/) ( #include <foo.h>), le compilateur n'émettra PAS d'avertissement (j'ai essayé 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]); \
} \
)
Maintenant, ARRAY_SIZE()c'est complètement sûr, et donc tous ses dérivés seront sûrs.
Mise à jour: libbsd fournit __arraycount():
Libbsd fournit la macro __arraycount()dans <sys/cdefs.h>, ce qui est dangereux parce qu'il manque une paire de parenthèses, mais nous pouvons ajouter ces parenthèses nous - mêmes, et donc nous ne même pas besoin d'écrire la division dans notre tête (pourquoi nous dupliquer du code qui existe déjà? ). Cette macro est définie dans un en-tête système, donc si nous l'utilisons, nous sommes obligés d'utiliser les macros ci-dessus.
Certains systèmes fournissent nitems()à la <sys/param.h>place et certains systèmes fournissent les deux. Vous devez vérifier votre système et utiliser celui que vous avez, et peut-être utiliser des conditions de préprocesseur pour la portabilité et prendre en charge les deux.
Mise à jour: autorisez la macro à être utilisée à la portée du fichier:
Malheureusement, l' ({})extension gcc ne peut pas être utilisée à la portée du fichier. Pour pouvoir utiliser la macro à la portée du fichier, l'assertion statique doit être à l'intérieur sizeof(struct {}). Ensuite, multipliez-le par 0pour ne pas affecter le résultat. Un cast to (int)pourrait être bon pour simuler une fonction qui retourne (int)0(dans ce cas, ce n'est pas nécessaire, mais il est réutilisable pour d'autres choses).
Pourriez-vous expliquer pourquoi le downvote? Cela montre une solution à une construction dangereuse et commune ( sizeof(arr)) qui ne figurent pas ailleurs: ARRAY_BYTES(arr).
Cacahuete Frito
2
ARRAY_SIZE est suffisamment commun pour être utilisé librement, et ARRAY_BYTES est très explicite dans son nom, devrait être défini à côté de ARRAY_SIZE afin qu'un utilisateur puisse voir les deux facilement et par son utilisation, je ne pense pas que quiconque lisant le code ait des doutes sur ce Cela fait. Ce que je voulais dire, c'est de ne pas utiliser un simple sizeof, mais d'utiliser ces constructions à la place; si vous avez envie d'écrire ces constructions à chaque fois, vous allez probablement faire une erreur (très fréquent si vous copiez-collez, et aussi très fréquent si vous les écrivez à chaque fois car elles ont beaucoup de parenthèses) ...
Cacahuete Frito
3
..., donc je me tiens sur la conclusion principale: un single sizeofest clairement dangereux (les raisons sont dans la réponse), et ne pas utiliser de macros mais utiliser les constructions que j'ai fournies, à chaque fois, est encore plus dangereux, donc la seule voie à suivre est des macros.
Cacahuete Frito
3
@MarkHarrison Je connais la différence entre les pointeurs et les tableaux. Mais il y a eu des fois où j'ai eu une fonction que j'ai plus tard refactorisée en petites fonctions, et ce qui était d'abord un tableau, plus tard était un pointeur, et c'est un point où si vous oubliez de changer la taille de, vous la vissez, et il est facile de ne pas voir l'un de ces.
Cacahuete Frito
3
@hyde Aussi que je sais que la différence ne signifie pas que tout le monde connaît la différence, et pourquoi ne pas utiliser quelque chose qui supprime fondamentalement 100% de ces bugs? Ce bogue est presque arrivé à Linux; il est arrivé à Linus, ce qui signifie qu'il a passé beaucoup de contrôle, et cela signifie également qu'il est possible que le même bogue soit entré dans Linux dans une autre partie du noyau, comme il le dit.
Cacahuete Frito
11
"vous avez introduit une manière subtile de vous tirer une balle dans le pied"
Les tableaux «natifs» ne stockent pas leur taille. Il est donc recommandé d'enregistrer la longueur du tableau dans une variable / const distincte et de la transmettre chaque fois que vous passez le tableau, c'est-à-dire:
Vous DEVEZ toujours éviter les tableaux natifs (sauf si vous ne pouvez pas, auquel cas, faites attention à votre pied). Si vous écrivez en C ++, utilisez le conteneur 'vector' de la STL . "Par rapport aux tableaux, ils fournissent presque les mêmes performances", et ils sont bien plus utiles!
// 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();
Notez que cela ne fonctionne que pour les tableaux réels, pas les pointeurs qui pointent vers des tableaux.
David Schwartz
5
Si vous voulez vraiment faire cela pour faire circuler votre tableau, je suggère d'implémenter une structure pour stocker un pointeur sur le type dont vous voulez un tableau et un entier représentant la taille du tableau. Ensuite, vous pouvez transmettre cela à vos fonctions. Attribuez simplement la valeur de la variable du tableau (pointeur au premier élément) à ce pointeur. Ensuite, vous pouvez aller Array.arr[i]chercher le i-ème élément et utiliser Array.sizepour obtenir le nombre d'éléments dans le tableau.
J'ai inclus du code pour vous. Ce n'est pas très utile mais vous pouvez l'étendre avec plus de fonctionnalités. Pour être honnête, si ce sont les choses que vous voulez, vous devez cesser d'utiliser C et utiliser un autre langage avec ces fonctionnalités intégrées.
/* 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;}
Impossible de voter avec du code qui ne strcpy(d.name, name);gère pas le débordement.
chux
4
La meilleure façon est d'enregistrer ces informations, par exemple, dans une structure:
typedefstruct{int*array;int elements;} list_s;
Implémentez toutes les fonctions nécessaires telles que créer, détruire, vérifier l'égalité et tout ce dont vous avez besoin. Il est plus facile de passer en paramètre.
La fonction sizeofrenvoie le nombre d'octets utilisé par votre tableau dans la mémoire. Si vous souhaitez calculer le nombre d'éléments dans votre tableau, vous devez diviser ce nombre par le sizeoftype de variable du tableau. Disons que int array[10];si le nombre entier de type variable de votre ordinateur est de 32 bits (ou 4 octets), pour obtenir la taille de votre tableau, vous devez procéder comme suit:
Vous pouvez utiliser l' &opérateur. Voici le code source:
#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;};
Voici l'exemple de sortie
15492166721549216712---- diff----4The size of array a is 10
Je n'ai pas downvote, mais c'est comme frapper un clou avec une brique parce que vous n'avez pas remarqué un marteau à côté de vous. De plus, les gens ont tendance à froncer les sourcils en utilisant des variables non initialisées ... mais ici, je suppose que cela sert assez bien votre objectif.
Dmitri
2
@Dmitri aucune variable non initialisée n'est accessible ici
MM
1
Hmmm. La soustraction du pointeur mène à ptrdiff_t. sizeof()résulte en size_t. C ne définit pas ce qui est plus large ou plus élevé / même rang. Le type de quotient ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))n'est donc pas certain size_tet donc l'impression avec zpeut conduire à UB. Une simple utilisation printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);est suffisante.
chux
1
(char *)(&a+1)-(char *)an'est pas une constante et peut être calculée au moment de l'exécution, même avec une taille fixe a[10]. sizeof(a)/sizeof(a[0])est constant fait au moment de la compilation dans ce cas.
Outre les réponses déjà fournies, je voudrais souligner un cas particulier en utilisant
sizeof(a)/sizeof(a[0])
Si aest soit un tableau de char, unsigned charsoit signed charvous n'avez pas besoin de l'utiliser sizeofdeux fois car une sizeofexpression avec un opérande de ces types résulte toujours en 1.
Citation de C18,6.5.3.4 / 4:
« Quand sizeofon applique sur un opérande qui est de type char, unsigned char, ou signed char, (ou une version qualifiée de celui - ci) , le résultat est 1».
Ainsi, sizeof(a) / sizeof (a[0])serait équivalent à NUMBER OF ARRAY ELEMENTS / 1if aest un tableau de type char, unsigned charou signed char. La division par 1 est redondante.
Dans ce cas, vous pouvez simplement abréger et faire:
sizeof(a)
Par exemple:
char a[10];size_t length =sizeof(a);
Si vous voulez une preuve, voici un lien vers GodBolt .
Néanmoins, la division maintient la sécurité, si le type change de manière significative (bien que ces cas soient rares).
Vous préférez probablement appliquer une macro à la division, car le type peut changer à l'avenir (bien que cela soit peu probable), et la division est connue au moment de la compilation, donc le compilateur l'optimisera (s'il ne change pas, veuillez votre compilateur).
Cacahuete Frito
1
@CacahueteFrito Oui, j’y ai pensé aussi entre-temps. Je l'ai pris comme note secondaire dans la réponse. Je vous remercie.
RobertS soutient Monica Cellio le
-1
Remarque: Celui-ci peut vous donner un comportement indéfini comme indiqué par MM dans le commentaire.
Il s'agit d'un comportement techniquement indéfini; l' *opérateur ne peut pas être appliqué à un pointeur passé la fin
MM
3
"comportement indéfini" signifie que la norme C ne définit pas le comportement. Si vous l'essayez dans votre programme, alors tout peut arriver
MM
@MM dites-vous que *(&a+1) - a;c'est différent de ce qui (&a)[1] - a;précède, n'est-ce pas les deux *(&a+1)et (&a)[1]comptez comme 1 après la fin?
QuentinUK
@QuentinUK vos deux expressions sont les mêmes, x[y]est défini comme*(x + (y))
MM
@MM Je le pensais. Mais l'autre réponse, par Arjun Sreedharan, a 38 flèches vers le haut et cela a -1. Et la réponse d'Arjun Sreedharan ne fait aucune mention d'un comportement indéfini.
Réponses:
Résumé:
Réponse complète:
Pour déterminer la taille de votre tableau en octets, vous pouvez utiliser l'
sizeof
opérateur:Sur mon ordinateur, les entiers font 4 octets, donc n est 68.
Pour déterminer le nombre d'éléments dans le tableau, nous pouvons diviser la taille totale du tableau par la taille de l'élément du tableau. Vous pouvez le faire avec le type, comme ceci:
et obtenez la bonne réponse (68/4 = 17), mais si le type de
a
changement vous aurait un méchant bug si vous oubliez de le changersizeof(int)
aussi.Ainsi, le diviseur préféré est
sizeof(a[0])
ou l'équivalentsizeof(*a)
, la taille du premier élément du tableau.Un autre avantage est que vous pouvez désormais facilement paramétrer le nom du tableau dans une macro et obtenir:
la source
ARRAYSIZE
makro défini dansWinNT.h
(qui est tiré par d'autres en-têtes). Les utilisateurs de WinAPI n'ont donc pas besoin de définir leur propre makro.static int a[20];
. Mais votre commentaire est utile aux lecteurs qui ne réalisent pas la différence entre un tableau et un pointeur.La
sizeof
manière est la bonne, si vous traitez des tableaux non reçus comme paramètres. Un tableau envoyé en tant que paramètre à une fonction est traité comme un pointeur, ilsizeof
retournera donc la taille du pointeur au lieu de celle du tableau.Ainsi, à l'intérieur des fonctions, cette méthode ne fonctionne pas. Au lieu de cela, passez toujours un paramètre supplémentaire
size_t size
indiquant le nombre d'éléments dans le tableau.Tester:
Sortie (dans un système d'exploitation Linux 64 bits):
Sortie (dans un système d'exploitation Windows 32 bits):
la source
length of parameter:2
si seulement un pointeur vers le 1er élément du tableau est passé?(sizeof array / sizeof *array)
.Il convient de noter que cela
sizeof
n'aide pas lorsqu'il s'agit d'une valeur de tableau qui s'est désintégrée en un pointeur: même si elle pointe vers le début d'un tableau, pour le compilateur, c'est la même chose qu'un pointeur vers un seul élément de ce tableau . Un pointeur ne "se souvient" de rien d'autre du tableau qui a été utilisé pour l'initialiser.la source
char
32 bits. Tout ce que la norme dit, c'est que les valeurs entières de 0 à 127 peuvent être représentées, et sa plage est au moins de -127 à 127 (le caractère est signé) ou de 0 à 255 (le caractère est non signé).La taille du "truc" est le meilleur moyen que je connaisse, avec un petit mais (pour moi, ce qui est une bête noire majeure) un changement important dans l'utilisation des parenthèses.
Comme l'indique clairement l'entrée Wikipedia, les C ne
sizeof
sont pas une fonction; c'est un opérateur . Ainsi, il ne nécessite pas de parenthèses autour de son argument, sauf si l'argument est un nom de type. Ceci est facile à retenir, car il fait ressembler l'argument à une expression cast, qui utilise également des parenthèses.Donc: si vous avez les éléments suivants:
Vous pouvez trouver le nombre d'éléments avec du code comme celui-ci:
Pour moi, cela se lit beaucoup plus facilement que l'alternative avec parenthèses. Je suis également favorable à l'utilisation de l'astérisque dans la partie droite de la division, car il est plus concis que l'indexation.
Bien sûr, tout cela est également au moment de la compilation, il n'est donc pas nécessaire de s'inquiéter de la division affectant les performances du programme. Utilisez donc ce formulaire partout où vous le pouvez.
Il est toujours préférable d'utiliser sizeof sur un objet réel lorsque vous en avez un, plutôt que sur un type, car vous n'avez alors pas à vous soucier de faire une erreur et d'indiquer le mauvais type.
Par exemple, supposons que vous ayez une fonction qui génère des données sous forme de flux d'octets, par exemple sur un réseau. Appelons la fonction
send()
et faisons en sorte qu'elle prenne comme arguments un pointeur vers l'objet à envoyer et le nombre d'octets dans l'objet. Ainsi, le prototype devient:Et puis vous devez envoyer un entier, donc vous le codez comme ceci:
Maintenant, vous avez introduit une manière subtile de vous tirer une balle dans le pied, en spécifiant le type de
foo
à deux endroits. Si l'un change mais pas l'autre, le code se casse. Ainsi, faites-le toujours comme ceci:Maintenant tu es protégé. Bien sûr, vous dupliquez le nom de la variable, mais cela a une forte probabilité de se casser d'une manière que le compilateur peut détecter, si vous la changez.
la source
sizeof(int)
moins d'instructions quesizeof(foo)
?int x = 1+1;
versusint x = (1+1);
. Ici, les parenthèses sont purement absolument juste esthétiques.sizeof
sera toujours constant en C ++ et C89. Avec les tableaux à longueur variable de C99, il peut être évalué au moment de l'exécution.sizeof
peut être un opérateur mais il doit être traité comme une fonction selon Linus Torvalds. Je suis d'accord. Lisez son rationnel ici: lkml.org/lkml/2012/7/11/103Consultez ce lien pour des explications
la source
ptrdiff_t
. (Généralement sur un système 64 bits, ce sera un type plus grand queint
). Même si vous passezint
àptrdiff_t
dans ce code, il a toujours un bogue s'ilarr
occupe plus de la moitié de l'espace d'adressage./3G
option vous avez un partage utilisateur / noyau 3G / 1G, ce qui vous permet d'avoir une taille de tableau pouvant atteindre 75% de la taille de l'espace d'adressage.foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
comme variables globales.buf3[]
échoue car ce(&buf1)[1] - buf1
n'est pas une constante.Vous pouvez utiliser l'opérateur sizeof mais cela ne fonctionnera pas pour les fonctions car il prendra la référence du pointeur, vous pouvez faire ce qui suit pour trouver la longueur d'un tableau:
Code trouvé à l'origine ici: programme C pour trouver le nombre d'éléments dans un tableau
la source
Si vous connaissez le type de données du tableau, vous pouvez utiliser quelque chose comme:
Ou si vous ne connaissez pas le type de données du tableau, vous pouvez utiliser quelque chose comme:
Remarque: Cette chose ne fonctionne que si le tableau n'est pas défini au moment de l'exécution (comme malloc) et que le tableau n'est pas passé dans une fonction. Dans les deux cas,
arr
(nom du tableau) est un pointeur.la source
int noofele = sizeof(arr)/sizeof(int);
n'est qu'à mi-chemin meilleur que le codageint noofele = 9;
. L'utilisationsizeof(arr)
maintient la flexibilité en cas de modification de la taille du tableau. Encoresizeof(int)
faut-il une mise à jour en cas dearr[]
changement de type . Mieux vaut l'utilisersizeof(arr)/sizeof(arr[0])
même si le type est bien connu. On ne sait pas pourquoi utiliserint
pournoofele
vssize_t
, le type renvoyé parsizeof()
.La macro
ARRAYELEMENTCOUNT(x)
que tout le monde utilise n'évalue pas correctement . En réalité, ceci est juste une question sensible, car vous ne pouvez pas avoir d'expressions qui aboutissent à un type «tableau».Évalue en fait:
Tandis que
Il évalue correctement:
Cela n'a vraiment pas grand-chose à voir avec la taille des tableaux explicitement. Je viens de remarquer beaucoup d'erreurs de ne pas vraiment observer le fonctionnement du préprocesseur C. Vous encapsulez toujours le paramètre de macro dans lequel aucune expression ne peut être impliquée.
C'est correct; mon exemple était mauvais. Mais c'est exactement ce qui devrait arriver. Comme je l'ai mentionné précédemment, cela
p + 1
se terminera comme un type de pointeur et invalidera la macro entière (comme si vous tentiez d'utiliser la macro dans une fonction avec un paramètre de pointeur).À la fin de la journée, dans ce cas particulier , la faute n'a pas vraiment d'importance (donc je perds juste le temps de tout le monde; huzzah!), Parce que vous n'avez pas d'expressions avec un type de 'tableau'. Mais vraiment, le point sur les subtils d'évaluation du préprocesseur est, je pense, important.
la source
(sizeof (x) / sizeof (*x))
?Pour les tableaux multidimensionnels, c'est un peu plus compliqué. Souvent, les gens définissent des constantes macro explicites, c.-à-d.
Mais ces constantes peuvent également être évaluées au moment de la compilation avec sizeof :
Notez que ce code fonctionne en C et C ++. Pour les tableaux de plus de deux dimensions, utilisez
etc., à l'infini.
la source
la source
array
a, vous n'avez pas besoin d'utilisersizeof(array) / sizeof(array[0])
siarray
est un tableau de deuxchar
,unsigned char
ousigned char
- Citation de C18,6.5.3.4 / 4: « Lorsque sizeof est appliqué à un opérande qui est de type char, unsigned char, ou char signé , (ou une version qualifiée de celui-ci) le résultat est 1. " Dans ce cas, vous pouvez simplement fairesizeof(array)
comme expliqué dans ma réponse dédiée .Taille d'un tableau en C:
la source
size_t size_of_element
encore utiliséint
avecint n = sizeof (a) / sizeof (a[0]);
et nonsize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
,int n
tel qu'utilisé dansint n = sizeof (a) / sizeof (a[0]);
est insuffisant (c'est UB). L'utilisationsize_t n = sizeof (a) / sizeof (a[0]);
n'entraîne pas ce problème.Je conseillerais de ne jamais utiliser
sizeof
(même s'il peut être utilisé) pour obtenir l'une des deux tailles différentes d'un tableau, soit en nombre d'éléments soit en octets, qui sont les deux derniers cas que je montre ici. Pour chacune des deux tailles, les macros illustrées ci-dessous peuvent être utilisées pour la rendre plus sûre. La raison est de rendre évidente l'intention du code à mainteneurs, et la différencesizeof(ptr)
desizeof(arr)
prime abord (qui écrit de cette façon est pas évident), de sorte que les insectes sont alors évident pour tout le monde à lire le code.TL; DR:
must_be_array(arr)
(défini ci-dessous) EST nécessaire en tant que-Wsizeof-pointer-div
buggy (en avril / 2020):Il y a eu des bugs importants concernant ce sujet: https://lkml.org/lkml/2015/9/3/428
Je ne suis pas d'accord avec la solution proposée par Linus, qui consiste à ne jamais utiliser la notation matricielle pour les paramètres des fonctions.
J'aime la notation de tableau comme documentation qu'un pointeur est utilisé comme tableau. Mais cela signifie qu'une solution infaillible doit être appliquée pour qu'il soit impossible d'écrire du code bogué.
À partir d'un tableau, nous avons trois tailles que nous pourrions vouloir savoir:
La taille des éléments du tableau
Le premier est très simple, et cela n'a pas d'importance si nous avons affaire à un tableau ou à un pointeur, car c'est fait de la même manière.
Exemple d'utilisation:
qsort()
a besoin de cette valeur comme troisième argument.Pour les deux autres tailles, qui sont le sujet de la question, nous voulons nous assurer que nous avons affaire à un tableau, et casser la compilation sinon, car si nous avons affaire à un pointeur, nous obtiendrons des valeurs erronées . Lorsque la compilation est interrompue, nous pourrons facilement voir que nous n'avons pas affaire à un tableau, mais à un pointeur à la place, et nous devrons simplement écrire le code avec une variable ou une macro qui stocke la taille du tableau derrière le pointeur.
Le nombre d'éléments dans le tableau
Celui-ci est le plus courant, et de nombreuses réponses vous ont fourni la macro typique ARRAY_SIZE:
Étant donné que le résultat de ARRAY_SIZE est couramment utilisé avec des variables de type signées
ptrdiff_t
, il est bon de définir une variante signée de cette macro:Les tableaux avec plus de
PTRDIFF_MAX
membres vont donner des valeurs invalides pour cette version signée de la macro, mais à la lecture de C17 :: 6.5.6.9, des tableaux comme celui-ci jouent déjà avec le feu. UniquementARRAY_SIZE
etsize_t
doit être utilisé dans ces cas.Les versions récentes de compilateurs, tels que GCC 8, vous avertiront lorsque vous appliquerez cette macro à un pointeur, elle est donc sûre (il existe d'autres méthodes pour la sécuriser avec des compilateurs plus anciens).
Il fonctionne en divisant la taille en octets de l'ensemble du tableau par la taille de chaque élément.
Exemples d'utilisation:
Si ces fonctions n'utilisaient pas de tableaux, mais les obtenaient à la place comme paramètres, l'ancien code ne se compilerait pas, il serait donc impossible d'avoir un bogue (étant donné qu'une version récente du compilateur est utilisée, ou qu'une autre astuce est utilisée) , et nous devons remplacer l'appel de macro par la valeur:
La taille en octets que le tableau utilise en mémoire
ARRAY_SIZE
est couramment utilisé comme solution au cas précédent, mais ce cas est rarement écrit en toute sécurité, peut-être parce qu'il est moins courant.La manière courante d'obtenir cette valeur est d'utiliser
sizeof(arr)
. Le problème: le même qu'avec le précédent; si vous avez un pointeur au lieu d'un tableau, votre programme deviendra fou.La solution au problème consiste à utiliser la même macro que précédemment, que nous savons être sûre (elle rompt la compilation si elle est appliquée à un pointeur):
Son fonctionnement est très simple: il annule la division qui le
ARRAY_SIZE
fait, donc après des annulations mathématiques, vous vous retrouvez avec une seulesizeof(arr)
, mais avec la sécurité supplémentaire de laARRAY_SIZE
construction.Exemple d'utilisation:
memset()
a besoin de cette valeur comme troisième argument.Comme précédemment, si le tableau est reçu en paramètre (un pointeur), il ne se compilera pas, et nous devrons remplacer l'appel de macro par la valeur:
Mise à jour (23 / avril / 2020):
-Wsizeof-pointer-div
est buggé :Aujourd'hui, j'ai découvert que le nouvel avertissement dans GCC ne fonctionne que si la macro est définie dans un en-tête qui n'est pas un en-tête système. Si vous définissez la macro dans un en-tête installé sur votre système (généralement
/usr/local/include/
ou/usr/include/
) (#include <foo.h>
), le compilateur n'émettra PAS d'avertissement (j'ai essayé GCC 9.3.0).Nous avons donc
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
et voulons le rendre sûr. Nous aurons besoin de C11_Static_assert()
et de quelques extensions GCC: déclarations et déclarations dans les expressions , __builtin_types_compatible_p :Maintenant,
ARRAY_SIZE()
c'est complètement sûr, et donc tous ses dérivés seront sûrs.Mise à jour: libbsd fournit
__arraycount()
:Libbsd fournit la macro
__arraycount()
dans<sys/cdefs.h>
, ce qui est dangereux parce qu'il manque une paire de parenthèses, mais nous pouvons ajouter ces parenthèses nous - mêmes, et donc nous ne même pas besoin d'écrire la division dans notre tête (pourquoi nous dupliquer du code qui existe déjà? ). Cette macro est définie dans un en-tête système, donc si nous l'utilisons, nous sommes obligés d'utiliser les macros ci-dessus.Certains systèmes fournissent
nitems()
à la<sys/param.h>
place et certains systèmes fournissent les deux. Vous devez vérifier votre système et utiliser celui que vous avez, et peut-être utiliser des conditions de préprocesseur pour la portabilité et prendre en charge les deux.Mise à jour: autorisez la macro à être utilisée à la portée du fichier:
Malheureusement, l'
({})
extension gcc ne peut pas être utilisée à la portée du fichier. Pour pouvoir utiliser la macro à la portée du fichier, l'assertion statique doit être à l'intérieursizeof(struct {})
. Ensuite, multipliez-le par0
pour ne pas affecter le résultat. Un cast to(int)
pourrait être bon pour simuler une fonction qui retourne(int)0
(dans ce cas, ce n'est pas nécessaire, mais il est réutilisable pour d'autres choses).la source
sizeof(arr)
) qui ne figurent pas ailleurs:ARRAY_BYTES(arr)
.sizeof
, mais d'utiliser ces constructions à la place; si vous avez envie d'écrire ces constructions à chaque fois, vous allez probablement faire une erreur (très fréquent si vous copiez-collez, et aussi très fréquent si vous les écrivez à chaque fois car elles ont beaucoup de parenthèses) ...sizeof
est clairement dangereux (les raisons sont dans la réponse), et ne pas utiliser de macros mais utiliser les constructions que j'ai fournies, à chaque fois, est encore plus dangereux, donc la seule voie à suivre est des macros."vous avez introduit une manière subtile de vous tirer une balle dans le pied"
Les tableaux «natifs» ne stockent pas leur taille. Il est donc recommandé d'enregistrer la longueur du tableau dans une variable / const distincte et de la transmettre chaque fois que vous passez le tableau, c'est-à-dire:
Vous DEVEZ toujours éviter les tableaux natifs (sauf si vous ne pouvez pas, auquel cas, faites attention à votre pied). Si vous écrivez en C ++, utilisez le conteneur 'vector' de la STL . "Par rapport aux tableaux, ils fournissent presque les mêmes performances", et ils sont bien plus utiles!
Voir: http://www.cplusplus.com/reference/stl/vector/
la source
enum
déclaration.la source
Si vous voulez vraiment faire cela pour faire circuler votre tableau, je suggère d'implémenter une structure pour stocker un pointeur sur le type dont vous voulez un tableau et un entier représentant la taille du tableau. Ensuite, vous pouvez transmettre cela à vos fonctions. Attribuez simplement la valeur de la variable du tableau (pointeur au premier élément) à ce pointeur. Ensuite, vous pouvez aller
Array.arr[i]
chercher le i-ème élément et utiliserArray.size
pour obtenir le nombre d'éléments dans le tableau.J'ai inclus du code pour vous. Ce n'est pas très utile mais vous pouvez l'étendre avec plus de fonctionnalités. Pour être honnête, si ce sont les choses que vous voulez, vous devez cesser d'utiliser C et utiliser un autre langage avec ces fonctionnalités intégrées.
la source
strcpy(d.name, name);
gère pas le débordement.La meilleure façon est d'enregistrer ces informations, par exemple, dans une structure:
Implémentez toutes les fonctions nécessaires telles que créer, détruire, vérifier l'égalité et tout ce dont vous avez besoin. Il est plus facile de passer en paramètre.
la source
int elements
vssize_t elements
?La fonction
sizeof
renvoie le nombre d'octets utilisé par votre tableau dans la mémoire. Si vous souhaitez calculer le nombre d'éléments dans votre tableau, vous devez diviser ce nombre par lesizeof
type de variable du tableau. Disons queint array[10];
si le nombre entier de type variable de votre ordinateur est de 32 bits (ou 4 octets), pour obtenir la taille de votre tableau, vous devez procéder comme suit:la source
Vous pouvez utiliser l'
&
opérateur. Voici le code source:Voici l'exemple de sortie
la source
ptrdiff_t
.sizeof()
résulte ensize_t
. C ne définit pas ce qui est plus large ou plus élevé / même rang. Le type de quotient((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
n'est donc pas certainsize_t
et donc l'impression avecz
peut conduire à UB. Une simple utilisationprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
est suffisante.(char *)(&a+1)-(char *)a
n'est pas une constante et peut être calculée au moment de l'exécution, même avec une taille fixea[10]
.sizeof(a)/sizeof(a[0])
est constant fait au moment de la compilation dans ce cas.La réponse la plus simple:
la source
Une solution plus élégante sera
la source
Outre les réponses déjà fournies, je voudrais souligner un cas particulier en utilisant
Si
a
est soit un tableau dechar
,unsigned char
soitsigned char
vous n'avez pas besoin de l'utilisersizeof
deux fois car unesizeof
expression avec un opérande de ces types résulte toujours en1
.Citation de C18,6.5.3.4 / 4:
Ainsi,
sizeof(a) / sizeof (a[0])
serait équivalent àNUMBER OF ARRAY ELEMENTS / 1
ifa
est un tableau de typechar
,unsigned char
ousigned char
. La division par 1 est redondante.Dans ce cas, vous pouvez simplement abréger et faire:
Par exemple:
Si vous voulez une preuve, voici un lien vers GodBolt .
Néanmoins, la division maintient la sécurité, si le type change de manière significative (bien que ces cas soient rares).
la source
Remarque: Celui-ci peut vous donner un comportement indéfini comme indiqué par MM dans le commentaire.
Pour plus de détails, voir ici et aussi ici .
la source
*
opérateur ne peut pas être appliqué à un pointeur passé la fin*(&a+1) - a;
c'est différent de ce qui(&a)[1] - a;
précède, n'est-ce pas les deux*(&a+1)
et(&a)[1]
comptez comme 1 après la fin?x[y]
est défini comme*(x + (y))