269 votes

C++ new int[0] -- va-t-il allouer de la mémoire ?

Une simple application de test :

cout << new int[0] << endl;

sorties :

0x876c0b8

On dirait que ça marche. Que dit la norme à ce sujet ? Est-il toujours légal d'"allouer" un bloc de mémoire vide ?

4 votes

+1 Question très intéressante - même si je ne suis pas sûr qu'elle ait beaucoup d'importance dans le code réel.

43 votes

@Zifre : Je pose la question par curiosité, mais cela pourrait avoir de l'importance dans le monde réel, par exemple lorsque la taille des blocs de mémoire alloués est calculée d'une certaine manière, et que le résultat du calcul peut être zéro, alors il n'y a pas de besoin direct d'ajouter des exceptions pour ne pas allouer des blocs de taille zéro . En effet, ils devraient être alloués et supprimés sans erreur (si seulement le bloc de taille zéro n'est pas déréférencé). D'une manière générale, cela permet d'élargir l'abstraction de ce qu'est un bloc de mémoire.

2 votes

@emg-2 : Dans votre exemple, cela n'aurait pas d'importance, car delete[] est parfaitement légal sur un pointeur NULL :-).

253voto

Faisal Vali Points 10048

De 5.3.4/7

Lorsque la valeur de l'expression dans un déclarateur direct-new est zéro, la fonction d'allocation est appelée pour allouer un tableau sans éléments.

De 3.7.3.1/2

L'effet du déréférencement d'un pointeur renvoyé comme une demande de taille zéro est indéfini.

Aussi

Même si la taille de l'espace demandé [par new] est nulle, la demande peut échouer.

Cela signifie que vous pouvez le faire, mais que vous ne pouvez pas légalement (d'une manière bien définie sur toutes les plateformes) déréférencer la mémoire que vous obtenez - vous pouvez seulement la passer à array delete - et vous devez la supprimer.

Voici une note de bas de page intéressante (c'est-à-dire qui n'est pas une partie normative de la norme, mais qui est incluse à des fins d'exposition) attachée à la phrase de 3.7.3.1/2

[32. L'intention est d'avoir l'opérateur new() implémentable en appelant malloc() ou calloc(), donc les règles sont substantiellement les mêmes. Le C++ diffère du C en exigeant qu'une requête zéro renvoie un pointeur non nul].

2 votes

J'obtiens une fuite de mémoire si je ne supprime pas. Est-ce que c'est prévu ? En tout cas, je ne m'y attendais pas.

12 votes

@EralpB : au contraire, même si votre demande est nulle, cette allocation se fait sur le Heap, où une demande implique plusieurs opérations de comptabilité, comme l'allocation et l'initialisation des gardes du Heap avant et après la zone donnée par l'allocateur, l'insertion dans des freelists ou d'autres structures horribles complexes. Le libérer signifie faire la comptabilité à l'envers.

3 votes

@EralpB oui je suppose que vous pouvez vous attendre à une fuite de mémoire chaque fois que vous n'équilibrez pas un new[] avec un delete[] - quelle que soit la taille. En particulier, lorsque vous appelez new[i] vous avez besoin d'un peu plus de mémoire que celle que vous essayez d'allouer pour stocker la taille du tableau (qui est ensuite utilisée par delete[] lors de la désallocation)

24voto

Oui, il est légal d'allouer un tableau de taille nulle comme ceci. Mais vous devez également le supprimer.

1 votes

Avez-vous une référence à ce sujet ? Nous savons tous que int ar[0]; est illégale, pourquoi la nouvelle est-elle acceptable ?

4 votes

Il est intéressant de constater que le C++ n'est pas aussi strict en ce qui concerne l'interdiction des objets de taille zéro. Pensez à l'optimisation de la classe de base vide : Ici aussi, un sous-objet de classe de base vide peut avoir une taille nulle. En revanche, la norme C s'efforce de garantir qu'aucun objet de taille zéro ne soit jamais créé : La définition de malloc(0) précise que l'effet est d'exécuter malloc avec un argument non nul, par exemple. Et dans struct { ... ; T n[] ; } ; lorsqu'il n'y a pas d'espace alloué pour le tableau (FAM), il est dit qu'il se comporte comme si "n" n'avait qu'un seul élément. (Dans les deux cas, l'utilisation de l'objet de quelque manière que ce soit est UB, comme x.n[0]).

1 votes

Je pense que sizeof (type) est censé ne jamais renvoyer zéro. Voir par exemple : stackoverflow.com/questions/2632021/can-sizeof-return-0-zero

15voto

ChrisW Points 37322

Que dit la norme à ce sujet ? Est-il toujours légal d'"allouer" un bloc de mémoire vide ?

Chaque objet a une identité unique, c'est-à-dire une adresse unique, ce qui implique une longueur non nulle (la quantité réelle de mémoire sera augmentée silencieusement, si vous demandez zéro octet).

Si vous attribuez plusieurs de ces objets, vous constaterez qu'ils ont des adresses différentes.

0 votes

"Chaque objet a une identité unique, c'est-à-dire une adresse unique, ce qui implique une longueur non nulle" - c'est vrai, mais c'est faux. L'objet a une adresse, mais le pointeur sur l'objet peut pointer sur une mémoire aléatoire. "La quantité réelle de mémoire sera augmentée silencieusement si vous demandez zéro octet" - pas sûr. operator [] stocke également la taille du tableau quelque part (voir isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Ainsi, si l'implémentation alloue le nombre d'octets avec les données, elle pourrait simplement allouer le nombre d'octets et 0 octet pour les données, ce qui renverrait le pointeur 1-past-last.

0 votes

Une longueur non nulle de la mémoire allouée, et non une longueur non nulle de la mémoire utilisable. Ce que je voulais dire, c'est que deux pointeurs vers deux objets distincts ne devraient pas pointer vers la même adresse.

1 votes

La norme ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ) indique en effet (3.7.4.1/2) que les différents appels à la fonction operator new[] doivent renvoyer des pointeurs différents. Par ailleurs, la fonction new expression comporte des règles supplémentaires (5.3.4). Je n'ai pas trouvé d'indice permettant d'affirmer que new avec une taille de 0 est en fait requis pour allouer quoi que ce soit. Désolé, j'ai rétrogradé parce que je trouve que votre réponse ne répond pas aux questions, mais fournit des déclarations controversées.

14voto

Evan Teran Points 42370

Oui, il est tout à fait légal d'allouer un bloc de taille 0 avec new. Vous ne pouvez simplement rien faire d'utile avec ce bloc puisqu'il n'y a aucune donnée valide à laquelle vous pouvez accéder. int[0] = 5; est illégale.

Cependant, je crois que la norme permet des choses comme malloc(0) pour retourner NULL.

Vous devrez toujours delete [] quel que soit le pointeur que vous récupérez de l'allocation aussi.

5 votes

En ce qui concerne malloc, vous avez raison - il est défini par l'implémentation. Cela est souvent considéré comme une erreur.

0 votes

Je suppose qu'une question intéressante se pose : la version nothrow de new peut-elle renvoyer NULL si on lui donne une taille de 0 ?

1 votes

La sémantique de new et de malloc n'est en aucun cas liée, du moins selon la norme.

2voto

shuaihanhungry Points 309

Curieusement, le C++ exige que l'opérateur new renvoie un même lorsque zéro octet est demandé. (L'exigence de ce comportement simplifie les choses ailleurs dans le langage).

J'ai trouvé C++ efficace Troisième édition comme suit : "Point 51 : Respecter les conventions lors de la rédaction de nouveaux documents et de la suppression de documents".

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