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

241

Une application de test simple:

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

les sorties:

0x876c0b8

Il semble donc que cela fonctionne. Que dit la norme à ce sujet? Est-il toujours légal "d'allouer" un bloc de mémoire vide?


la source
4
+1 Question très intéressante - même si je ne sais pas à quel point cela compte dans le vrai code.
Zifre
37
@Zifre: Je demande de la curiosité, mais cela peut 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 manière ou d'une autre et que le résultat du calcul peut être nul, il n'est pas nécessaire d'ajouter directement des exceptions pour ne pas allouer des blocs de taille nulle. Parce qu'ils doivent être alloués et supprimés sans erreur (si seul le bloc de taille zéro n'est pas déréférencé). Donc, généralement, cela donne une abstraction plus large de ce qu'est un bloc de mémoire.
2
@ emg-2: Dans votre situation d'exemple, cela n'aurait pas d'importance, car delete [] est parfaitement légal sur un pointeur NULL :-).
Evan Teran
2
Il n'est que tangentiellement lié - donc je commente ici - mais C ++ garantit à bien des égards que les objets distincts ont des adresses uniques ... même s'ils ne nécessitent pas explicitement de stockage. Une expérience similaire consisterait à vérifier la taille d'une structure vide. Ou un tableau de cette structure.
Drew Dormann
2
Pour développer le commentaire de Shmoopty: Surtout lors de la programmation avec des modèles (par exemple des modèles de classe de politique comme std :: allocator), il est courant en C ++ d'avoir des objets de taille nulle. Le code générique peut avoir besoin d'allouer dynamiquement de tels objets et d'utiliser des pointeurs vers eux pour comparer l'identité de l'objet. C'est pourquoi l'opérateur new () renvoie des pointeurs uniques pour les requêtes de taille nulle. Bien que sans doute moins important / commun, le même raisonnement s'applique à l'allocation de tableau et à l'opérateur new [] ().
Trevor Robinson

Réponses:

234

À partir du 5.3.4 / 7

Lorsque la valeur de l'expression dans un déclarant direct nouveau est nulle, la fonction d'allocation est appelée pour allouer un tableau sans éléments.

À partir du 3.7.3.1/2

L'effet de déréférencer un pointeur renvoyé comme une demande de taille nulle n'est pas défini.

Aussi

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

Cela signifie que vous pouvez le faire, mais vous ne pouvez pas légalement (d'une manière bien définie sur toutes les plates-formes) déréférencer la mémoire que vous obtenez - vous ne pouvez la transmettre qu'à la suppression de tableaux - et vous devez la supprimer.

Voici une note de bas de page intéressante (c'est-à-dire pas une partie normative de la norme, mais incluse à des fins d'exposé) attachée à la phrase du 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 sensiblement les mêmes. C ++ diffère de C en exigeant une demande nulle pour renvoyer un pointeur non nul.]

Faisal Vali
la source
Je reçois une fuite de mémoire si je ne supprime pas. Est-ce attendu? Au moins, je ne m'y attendais pas.
EralpB
12
@EralpB: au contraire, même si votre demande est nulle, cette allocation se produit sur le tas, où une demande implique plusieurs opérations de tenue de livres, comme l'allocation et l'initialisation des gardes de tas avant et après la zone donnée par l'allocateur, l'insertion dans des listes libres ou d'autres structures horribles complexes. Le libérer signifie faire la comptabilité à l'envers.
v.oddou
3
@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 afin de stocker la taille du tableau (qui sera ensuite utilisée delete[]lors de la désallocation)
pqnet
24

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


la source
1
Avez-vous une citation pour cela? Nous savons tous que int ar[0];c'est illégal pourquoi est nouveau OK?
Motti
4
Il est intéressant de noter que C ++ n'est pas si fort avec l'interdiction des objets de taille nulle. Pensez à l'optimisation de la classe de base vide: ici aussi, un sous-objet de classe de base vide peut avoir la taille zéro. En revanche, la norme C s'efforce fortement de ne jamais créer d'objets de taille nulle: lors de la définition de malloc (0), il indique que l'exécution de malloc avec un argument différent de zéro, par exemple. Et dans struct {...; T n []; }; lorsqu'il n'y a pas d'espace alloué pour le tableau (FAM), il dit qu'il se comporte comme si "n" avait un élément. (Dans les deux cas, l'utilisation de l'objet de quelque manière que ce soit est UB, comme xn [0])
Johannes Schaub - litb
1
Je pense que l' sizeof (type)on ne devrait jamais retourner zéro. Voir par exemple: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet
Même la soi-disant "optimisation de classe de base vide" n'est pertinente qu'en raison de l'insistance que tous les objets (par opposition à tous les octets dans les objets) doivent avoir des adresses uniques. C ++ aurait pu être simplifié si du code qui se souciait réellement des objets ayant des adresses uniques était nécessaire pour garantir qu'ils avaient une taille non nulle.
supercat
15

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 silencieusement augmentée, si vous demandez zéro octet).

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

ChrisW
la source
"Chaque objet a une identité unique, c'est-à-dire une adresse unique, ce qui implique une longueur non nulle" - vrai, mais faux. L'objet a une adresse, mais le pointeur vers l'objet peut pointer vers une mémoire aléatoire. "La quantité réelle de mémoire sera augmentée en silence, 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 ). Donc, 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, en renvoyant le pointeur un avant-dernier.
Steed
Une longueur non nulle de mémoire allouée, pas une longueur non nulle de mémoire utilisable. Mon point était que deux pointeurs vers deux objets distincts ne devraient pas pointer vers la même adresse.
ChrisW
1
La norme ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ) dit en effet (3.7.4.1/2) que différents appels à operator new[]doivent renvoyer différents pointeurs. BTW, le new expressiona quelques règles supplémentaires (5.3.4). Je n'ai trouvé aucun indice newqu'avec la taille 0 est réellement nécessaire pour allouer quoi que ce soit. Désolé, j'ai voté contre parce que je trouve que votre réponse ne répond pas aux questions, mais fournit des déclarations controversées.
Steed
@Steed la mémoire pour stocker la longueur du bloc peut être implicitement calculée en utilisant l'espace d'adressage. Pour les tableaux de taille nulle, par exemple, il pourrait être possible pour l' new[]implémentation de renvoyer des adresses dans une plage où il n'y a pas de mémoire dynamique mappée, donc n'utilisant pas vraiment de mémoire (tout en utilisant l'espace d'adressage)
pqnet
@pqnet: Une implémentation de bonne qualité devrait permettre un nombre illimité de cycles nouveaux / supprimer, n'est-ce pas? Sur une plate-forme avec des pointeurs 64 bits, il pourrait être raisonnable de dire qu'un ordinateur exécutant une while(!exitRequested) { char *p = new char[0]; delete [] p; }boucle sans recycler les pointeurs s'effondrerait avant de manquer d'espace d'adressage, mais sur une plate-forme avec des pointeurs 32 bits, ce serait un hypothèse beaucoup moins raisonnable.
supercat
14

Oui, il est tout à fait légal d'allouer un 0bloc de taille avec new. Vous ne pouvez tout simplement rien faire d'utile car il n'y a pas de données valides auxquelles vous pouvez accéder. int[0] = 5;est illégal.

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

Vous aurez également besoin du delete []pointeur que vous récupérez de l'allocation.

Evan Teran
la source
5
Concernant malloc, vous avez raison - c'est une implémentation définie. Ceci est souvent considéré comme une erreur.
Je suppose qu'une question intéressante est: la version nothrow du nouveau retourne-t-elle NULL si on lui donne une taille de 0?
Evan Teran
1
La sémantique de new et malloc n'est liée en aucune façon, du moins par la norme.
ne pas dire qu'ils sont ... demandait spécifiquement la version nothrow de new.
Evan Teran
@Evan - la version nothrow de new ne renvoie null que si la requête échoue - pas seulement si la taille de la requête est 0 @Neil - pas normativement liée - mais liée par intention (ie l'opérateur new peut être implémenté en termes de malloc mais pas l'autre) autour) - voir la note de bas de page que j'ai incluse dans ma réponse
Faisal Vali
1

Curieusement, C ++ nécessite que l'opérateur new retourne un pointeur légitime même lorsque zéro octet est demandé. (Exiger ce comportement à consonance étrange simplifie les choses ailleurs dans la langue.)

J'ai trouvé que la troisième édition efficace de C ++ disait comme ceci dans "Article 51: Adhérer à la convention lors de l'écriture de nouveaux et supprimer".

shuaihanhungry
la source
0

Je vous garantis que le nouvel int [0] vous coûte plus d'espace depuis que je l'ai testé.

Par exemple, l'utilisation de la mémoire de

int **arr = new int*[1000000000];

est significativement plus petit que

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

L'utilisation de la mémoire du deuxième extrait de code moins celle du premier extrait de code est la mémoire utilisée pour les nombreux nouveaux int [0].

Shi Jieming
la source