61 votes

C++ n'est pas de vous dire la taille d'un tableau dynamique. Mais pourquoi?

Je sais qu'il n'existe aucun moyen en C++ pour obtenir la taille d'une manière dynamique créée ensemble, telles que:

int* a;
a = new int[n];

Ce que je voudrais savoir, c'est: Pourquoi? Les gens ont juste oublier cela dans la spécification du langage C++, ou est-il une raison technique?

N'est-ce pas les informations stockées quelque part? Après tout, la commande

delete[] a;

semble savoir de combien de mémoire il a à la sortie, donc il me semble qu' delete[] a une façon de savoir la taille d' a.

38voto

Martin Bonner Points 91

C'est un prolongement de la règle fondamentale de "ne pas payer pour ce que vous n'avez pas besoin". Dans votre exemple, delete[] a; n'a pas besoin de connaître la taille du tableau, en raison de l'int de ne pas disposer d'un destructeur. Si vous aviez écrit:

std::string* a;
a = new std::string[n];
...
delete [] a;

Puis l' delete a appeler les destructeurs (et a besoin de savoir combien d'appels) - dans ce cas, l' new a pour but de sauver qui comptent. Toutefois, étant donné qu'il n'a pas besoin d'être sauvé, dans toutes les occasions, Bjarne décidé de ne pas donner accès.

(Avec le recul, je pense que c'était une erreur ...)

Même avec int bien sûr, quelque chose de a à savoir à propos de la taille de la mémoire allouée, mais:

  • De nombreux allocateurs de ronde de la taille de certaines pratiques multiples (disons 64 octets) pour l'alignement et la commodité des raisons. L'allocateur sait qu'un bloc est de 64 octets de long -, mais elle ne sait pas si c'est parce qu' n a 1 ... ou 16.

  • Le C++ de la bibliothèque d'exécution peuvent ne pas avoir accès à la taille du bloc alloué. Si par exemple, new et delete utilisez malloc et free sous le capot, puis le C++ de la bibliothèque a aucun moyen de connaître la taille d'un bloc retourné par malloc. (D'habitude, new et malloc font tous deux partie de la même bibliothèque - mais pas toujours.)

13voto

molbdnilo Points 9289

Une raison fondamentale est qu'il n'y a pas de différence entre un pointeur vers le premier élément d'un tableau alloué dynamiquement de T et un pointeur vers n'importe quel autre T.

Considérons un fictifs fonction qui renvoie le nombre d'éléments d'un pointeur pointe.
Appelons cela la "taille".

Sons vraiment sympa, non?

Si ce n'était pas pour le fait que tous les pointeurs sont créés égaux:

char* p = new char[10];
size_t ps = size(p+1);  // What?

char a[10] = {0};
size_t as = size(a);     // Hmm...
size_t bs = size(a + 1); // Wut?

char i = 0;
size_t is = size(&i);  // OK?

On pourrait dire que le premier devrait être 9, le deuxième 10, le tiers - 9, et le dernier 1, mais pour ce faire, vous devez ajouter un "taille de la balise" sur chaque objet.
Un char exigera 128 bits de stockage (en raison de l'alignement) sur une machine 64 bits. C'est seize fois plus que ce qui est nécessaire.
(Ci-dessus, le dix-tableau de caractères a aurait besoin d'au moins 168 octets.)

Cela peut être pratique, mais il est également inacceptable cher.

Vous pouvez bien sûr imaginer une version qui n'est bien défini si l'argument est vraiment un pointeur vers le premier élément d'une allocation dynamique par défaut operator new, mais ce n'est pas aussi utile qu'on pourrait le penser.

4voto

Carsten S Points 157

Vous avez raison, une partie du système sera de savoir quelque chose au sujet de la taille. Mais l'obtention de cette information n'est probablement pas couverts par l'API de gestion de la mémoire système (pensez - malloc/free), et la taille exacte que vous avez demandé ne peut pas être connu, car il peut avoir été arrondis.

2voto

Jean Louw Points 56

Il y a un curieux cas de surcharge de l' operator delete que j'ai trouvé dans la forme de:

void operator delete[](void *p, size_t size);

Le paramètre taille semble par défaut pour la taille (en octets) du bloc de mémoire à laquelle void *p points. Si cela est vrai, il est raisonnable d'au moins l'espoir qu'il a une valeur transmise par l'invocation de l' operator new et, par conséquent, n'ont simplement besoin d'être divisé par sizeof(type) pour fournir le nombre d'éléments stockés dans le tableau.

Comme pour le "pourquoi" de la partie de votre question, Martin's de la règle de "ne pas payer pour ce que vous n'avez pas besoin de" me semble le plus logique.

2voto

DewiW Points 36

Vous trouverez souvent que les gestionnaires de mémoire ne allouer de l'espace dans un certain multiple de 64 octets par exemple.

Ainsi, vous pouvez demander de new int[4], soit 16 octets, mais le gestionnaire de mémoire va allouer de 64 octets pour votre demande. Pour libérer cette mémoire, il n'a pas besoin de connaître la quantité de mémoire que vous avez demandé, mais seulement qu'il a alloué un bloc de 64 octets.

La question suivante peut-être, peut-il ne pas stocker la taille demandée? C'est une charge supplémentaire qui pas tout le monde est prêt à payer pour. Un Arduino Uno pour exemple a seulement 2 ko de RAM, et dans ce contexte, 4 octets pour chaque attribution d'un coup, devient importante.

Si vous avez besoin de cette fonctionnalité, alors vous avez std::vector (ou équivalent), ou vous avez des langages de niveau supérieur. C/C++ a été conçu pour vous permettre de travailler avec un peu de surcharge que vous choisissez de l'utiliser, ce qui est un exemple.

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