62 votes

Déterminer la taille d'un tableau C++ de manière programmatique ?

Cette question a été inspirée par une question similaire : Comment delete[] "connaît" la taille du tableau d'opérandes ?

Ma question est un peu différente : Existe-t-il un moyen de déterminer la taille d'un tableau C++ de manière programmatique ? Et si non, pourquoi ? Chaque fonction que j'ai vue qui prend un tableau nécessite également un paramètre entier pour lui donner la taille. Mais comme la question liée l'a souligné, delete[] doit connaître la taille de la mémoire à désallouer.

Considérons ce code C++ :

int* arr = new int[256];
printf("Size of arr: %d\n", sizeof(arr));

Cela imprime " Size of arr: 4 ", qui est juste la taille du pointeur. Ce serait bien d'avoir une fonction qui imprime 256, mais je ne pense pas qu'il en existe une en C++. (Encore une fois, une partie de la question est de savoir pourquoi elle n'existe pas).

Clarification : Je sais que si je déclarais le tableau sur la pile au lieu du tas (c'est-à-dire " int arr[256]; ") que le sizeof retournerait 1024 (longueur du tableau * sizeof(int)).

0 votes

En fait, si vous avez alloué le tableau sur la pile, l'opérateur sizeof renverrait 1024 -- ce qui est 256 (le nombre d'éléments) * 4 (la taille d'un élément individuel). (sizeof(arr)/sizeof(arr[0])) donnerait le résultat 256.

0 votes

Merci, j'ai négligé cela parce que j'utilisais en fait char[] dans mon code de test (et sizeof(char) == 1)

0 votes

Bien que ce ne soit qu'une hypothèse - puisque cela ne fonctionne pas - je dois signaler que vous auriez dû écrire printf("Size of arr: %d\n", sizeof(*arr)); au lieu de printf("Size of arr: %d\n", sizeof(*arr)); puisque vous souhaitez récupérer la taille du pointeur déréférencé.

70voto

Dima Points 19888

delete [] connaît la taille qui a été allouée. Cependant, cette connaissance réside dans le runtime ou dans le gestionnaire de mémoire du système d'exploitation, ce qui signifie qu'elle n'est pas disponible pour le compilateur pendant la compilation. Et sizeof() n'est pas une vraie fonction, elle est en fait évaluée à une constante par le compilateur, ce qu'il ne peut pas faire pour les tableaux alloués dynamiquement, dont la taille n'est pas connue pendant la compilation.

Aussi, considérez cet exemple :

int *arr = new int[256];
int *p = &arr[100];
printf("Size: %d\n", sizeof(p));

Comment le compilateur pourrait-il savoir quelle est la taille de l'élément p est ? La racine du problème est que les tableaux en C et C++ ne sont pas des objets de première classe. Ils se décomposent en pointeurs, et il n'y a aucun moyen pour le compilateur ou le programme lui-même de savoir si un pointeur pointe vers le début d'un morceau de mémoire alloué par new ou à un seul objet, ou à un endroit quelconque au milieu d'un morceau de mémoire alloué par new .

L'une des raisons en est que le C et le C++ laissent la gestion de la mémoire au programmeur et au système d'exploitation, ce qui explique aussi pourquoi ils ne disposent pas de garbage collection. La mise en œuvre de new y delete ne fait pas partie de la norme C++, car le C++ est destiné à être utilisé sur une variété de plates-formes, qui peuvent gérer leur mémoire de manière très différente. Il peut être possible de laisser le C++ garder la trace de tous les tableaux alloués et de leurs tailles si vous écrivez un traitement de texte pour une boîte Windows fonctionnant avec le dernier CPU Intel, mais cela peut être complètement infaisable si vous écrivez un système embarqué fonctionnant avec un DSP.

7 votes

Il existe absolument des tableaux en C++. Sinon, comment expliquer qu'avec ce "char x[4] ; size_t sz = sizeof(x) ;", 'sz' se verra attribuer 4 ?

2 votes

Kevin, x n'est pas vraiment un tableau. C'est un pointeur vers un morceau de mémoire, qui se trouve sur la pile. Et parce qu'il a été alloué statiquement sur la pile, le compilateur connaît sa taille. Si vous passez x à une fonction, celle-ci ne connaîtra pas sa taille. Les tableaux en C ou C++ ne sont pas des objets de première classe.

0 votes

Juste à titre d'information : sizeof est un opérateur et non une fonction, comme l'a fait remarquer l'auteur de l'affiche qui a déclaré que ce n'était pas une vraie fonction.

19voto

James Curran Points 55356

Non, il n'y a aucun moyen de le faire en C++ standard.

À ma connaissance, il n'y a pas de raison valable de ne pas le faire. Probablement, la taille a été considérée comme un détail d'implémentation, et il est préférable de ne pas l'exposer. Notez que lorsque vous dites malloc(1000), il n'y a aucune garantie que le bloc retourné fasse 1000 octets --- seulement qu'il est au moins 1000 octets. Le plus souvent, il s'agit de 1020 (1K moins 4 octets pour l'overhead). Dans ce cas, la taille "1020" est celle dont la bibliothèque d'exécution doit se souvenir. Et bien sûr, cela peut changer entre les implémentations.

C'est pourquoi le comité des normes a ajouté std:vector<>, qui garde la trace de sa taille exacte.

4 votes

Une chose à noter est que new[] stocke également le nombre d'éléments demandés, afin d'appeler le nombre correct de constructeurs et de destructeurs pour le tableau. L'endroit où il est stocké est encore une fois spécifique à l'implémentation. La raison de ne pas inclure un moyen de l'obtenir est au-delà de mes connaissances.

1 votes

Je pense que la "bonne raison" est que les tableaux ne sont pas du tout des objets. Un tableau est juste un bloc de mémoire brut. La taille est une donnée de gestion de la mémoire, pas une donnée d'objet. Vous pourriez écrire une classe Array qui garderait la trace de la mémoire et de la taille, mais vous pourriez simplement utiliser std::vector et ne pas vous en soucier.

3 votes

Aha... Bien sûr. Un int* ne peut pas savoir si le tableau vers lequel il pointe est un nouveau tableau, un tableau local ou un point au milieu du tableau.

17voto

João Augusto Points 1614

Il existe en fait un moyen de déterminer la taille, mais il n'est pas "sûr" et sera différent d'un compilateur à l'autre..... il ne devrait donc pas être utilisé du tout .

Quand vous le faites : int* arr = new int[256] ;

Le 256 n'est pas pertinent, on vous donnera 256*sizeof(int) en supposant pour ce cas 1024, cette valeur sera probablement stockée à ( arr - 4 )

Donc pour vous donner le nombre d'"articles"

int* p_iToSize = arr - 4 ;

printf("Nombre d'éléments %d", *p_iToSize / sizeof(int)) ;

Pour chaque malloc, new, quoi que ce soit avant le bloc de mémoire continu que vous recevez, il est également alloué un espace réservé avec quelques informations concernant le bloc de mémoire qui vous a été donné.

10 votes

Néanmoins, cela répond en fait à la question.

1 votes

Intéressant :) pour ajouter 2 centimes, vous pourriez surcharger "new" et implémenter la gestion de la mémoire comme vous le souhaitez, vous pourriez l'avoir comme joao le décrit, ou stocker chaque pointeur dans une carte avec sa taille correspondante... en bref, il y a beaucoup de façons folles de le réaliser, mais je ne les utiliserais pas :p

0 votes

Qu'en est-il des tableaux de chars ? char * arr = new char[100] ;

5voto

Doug T. Points 33360

Le moyen le plus courant de gérer cette situation est d'utiliser un vecteur

int main()
{
   std::vector<int> v(256);
   printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity());
}

ou prédéfinir la taille

const int arrSize = 256;
int main()
{
    int array[arrSize];
    printf("Size of array is %i", sizeof(int) * arrSize);
}

0 votes

sizeof(int) * arrSize est identique à malloc('sizeof(int) * arrSize') n'est-ce pas ?

3voto

bearvarine Points 51

En fonction de votre application, vous pouvez créer une "valeur sentinelle" à la fin de votre tableau.

La valeur sentinelle doit avoir une propriété unique.

Vous pouvez ensuite traiter le tableau (ou effectuer une recherche linéaire) pour trouver la valeur sentinelle, en comptant au fur et à mesure. Une fois que vous avez atteint la valeur sentinelle, vous avez votre compte de tableau.

Pour une simple chaîne C, la terminaison \0 est un exemple de valeur sentinelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X