D'après ce qui est écrit ici , new
alloue dans le magasin gratuit tandis que malloc
utilise tas et les deux termes signifient souvent la même chose.
D'après ce qui est écrit ici , realloc
peut déplacer le bloc de mémoire vers un nouvel emplacement. Si le stockage gratuit et le tas sont deux espaces mémoire différents, cela signifie-t-il alors un problème?
Plus précisément, j'aimerais savoir si son utilisation est sûre
int* data = new int[3];
// ...
int* mydata = (int*)realloc(data,6*sizeof(int));
Sinon, existe-t-il un autre moyen de realloc
mémoire allouée avec new
en toute sécurité? Je pourrais allouer une nouvelle zone et memcpy
le contenu, mais d'après ce que je comprends, realloc
peut utiliser la même zone si possible.
Vous ne pouvez que realloc
ce qui a été alloué via malloc
(ou famille, comme calloc
).
En effet, les structures de données sous-jacentes qui gardent une trace des zones de mémoire libres et utilisées peuvent être très différentes.
C'est probablement mais en aucun cas garanti que C++ new
et C malloc
utilisent le même allocateur sous-jacent, auquel cas realloc
pourrait fonctionner pour les deux. Mais formellement, c'est en UB-land. Et dans la pratique, c'est juste inutilement risqué.
C++ n'offre pas de fonctionnalité correspondant à realloc
.
La plus proche est la réallocation automatique des (tampons internes des) conteneurs comme std::vector
.
Les conteneurs C++ souffrent d'être conçus d'une manière qui exclut l'utilisation de realloc
.
Au lieu du code présenté
int* data = new int[3];
//...
int* mydata = (int*)realloc(data,6*sizeof(int));
… Faites ceci:
vector<int> data( 3 );
//...
data.resize( 6 );
Cependant, si vous avez absolument besoin de l'efficacité générale de realloc
, et si vous devez accepter new
pour l'allocation d'origine, alors votre seul recours pour l'efficacité est d'utiliser des moyens spécifiques au compilateur, sachant que realloc
est sûr avec ce compilateur.
Sinon, si vous avez absolument besoin de l'efficacité générale de realloc
mais n'êtes pas obligé d'accepter new
, alors vous pouvez utiliser malloc
et realloc
. L'utilisation de pointeurs intelligents vous permet alors d'obtenir la même sécurité qu'avec les conteneurs C++.
La seule restriction éventuellement pertinente que C++ ajoute à realloc
est que les malloc
/calloc
/realloc
de C++ ne doivent pas être implémentés en termes de ::operator new
, et son free
ne doit pas être implémenté en termes de ::operator delete
(par C++ 14 [c.malloc] p3-4).
Cela signifie que la garantie que vous recherchez n'existe pas en C++. Cela signifie également, cependant, que vous pouvez implémenter ::operator new
En termes de malloc
. Et si vous faites cela, alors en théorie, le résultat de ::operator new
Peut être passé à realloc
.
En pratique, vous devez vous inquiéter de la possibilité que le résultat de new
ne corresponde pas au résultat de ::operator new
. Les compilateurs C++ peuvent par exemple combiner plusieurs expressions new
pour utiliser un seul appel ::operator new
. C'est quelque chose que les compilateurs ont déjà fait lorsque la norme ne le permettait pas, IIRC, et la norme le permet maintenant (selon C++ 14 [expr.new] p10). Cela signifie que même si vous suivez cette route, vous n'avez toujours pas la garantie que le passage de vos pointeurs new
vers realloc
a quelque chose de significatif, même si ce n'est plus un comportement indéfini.
En général, ne faites pas ça. Si vous utilisez des types définis par l'utilisateur avec initialisation non triviale , en cas de libération de copie de réallocation, le destructeur de vos objets a gagné pas appelé par realloc
. La copie le constructeur ne sera pas appelé aussi, lors de la copie. Cela peut conduire à un comportement indéfini en raison d'une utilisation incorrecte de la durée de vie de l'objet (voir Norme C++ §3.8 Durée de vie de l'objet, [basic.life] ).
1 La durée de vie d'un objet est une propriété d'exécution de l'objet. Un objet est dit avoir une initialisation non triviale s'il est d'un type classe ou agrégat et que lui ou l'un de ses membres est initialisé par un constructeur autre qu'un constructeur par défaut trivial. [Remarque: l'initialisation par un constructeur de copie/déplacement trivial est une initialisation non triviale. —Fin note]
La durée de vie d'un objet de type T commence lorsque:
- un stockage avec l'alignement et la taille appropriés pour le type T est obtenu, et
- si l'objet a une initialisation non triviale, son initialisation est terminée.
La durée de vie d'un objet de type T se termine lorsque:
- si T est un type de classe avec un destructeur non trivial (12.4), l'appel du destructeur démarre, ou
- le stockage occupé par l'objet est réutilisé ou libéré.
Et plus tard (c'est moi qui souligne):
3 Les propriétés attribuées aux objets dans la présente Norme internationale s'appliquent à un objet donné niquement pendant sa durée de vie.
Donc, vous ne voulez vraiment pas utiliser un objet hors de sa durée de vie.
Ce n'est pas sûr et ce n'est pas élégant.
Il peut être possible de remplacer new/delete pour prendre en charge la réaffectation, mais vous pouvez également envisager d'utiliser les conteneurs.
Oui - si new
a effectivement appelé malloc
en premier lieu (par exemple, c'est ainsi que VC++ new
fonctionne).
Non sinon. notez qu'une fois que vous décidez de réallouer la mémoire (parce que new
appelé malloc
), votre code est spécifique au compilateur et n'est plus portable entre les compilateurs.
(Je sais que cette réponse peut contrarier de nombreux développeurs, mais je réponds que cela dépend de faits réels, pas seulement de l'idiomatie).
Ce n'est pas sûr. Tout d'abord, le pointeur que vous passez à realloc
doit avoir été obtenu à partir de malloc
ou realloc
: http://en.cppreference.com/w/cpp/memory/c/realloc .
Deuxièmement, le résultat de new int [3]
n'a pas besoin d'être le même que le résultat de la fonction d'allocation - un espace supplémentaire peut être alloué pour stocker le nombre d'éléments.
(Et pour des types plus complexes que int
, realloc
ne serait pas sûr car il n'appelle pas les constructeurs de copie ou de déplacement.)
En général, non.
Il y a un tas de choses qui doivent tenir pour le rendre sûr:
Les types triviaux satisfont aux exigences ci-dessus.
En outre:
new[]
- la fonction doit transmettre la demande à malloc
sans aucune modification, ni aucune comptabilité sur le côté. Vous pouvez forcer cela en remplaçant global new [] et en supprimant [], ou ceux des classes respectives.Vous pouvez peut-être (pas dans tous les cas), mais vous ne devriez pas. Si vous devez redimensionner votre tableau de données, vous devez utiliser std::vector
au lieu.
Les détails sur la façon de l'utiliser sont listés dans un autre SO question .