2010-08-10 2 views
3

Si * get_ii() renvoyait de la mémoire, plutôt que de stocker de la mémoire, ce problème serait-il éliminé?résistance du pointeur

01 int *get_ii() 
02 { 
03 int ii;  // Local stack variable 
04 ii = 2; 
05 return ⅈ 
06 } 
07 main() 
08 { 
09 int *ii; 
10 ii = get_ii(); // After this call the stack is given up by the routine 
11     // get_ii() and its values are no longer safe. 
12  
13 ... Do stuff 
14 .. ii may be corrupt by this point. 
15 } 

Source - http://www.yolinux.com/TUTORIALS/C++MemoryCorruptionAndMemoryLeaks.html

grâce

+0

Même problème avec le renvoi d'une référence, la durée de vie d'un objet automatique se termine avec la portée. Dans la plupart des cas, renvoyez simplement une valeur à la place.(Ne pas * pré-optimiser et dire "oh, mais alors je vais copier un gros objet donc je ne devrais pas faire ça! Je vais le prendre par référence en tant que paramètre ou retourner un pointeur vers une valeur allouée dynamiquement Cependant, ne le faites pas.) – GManNickG

Répondre

7

Oui. Allouer du tas fonctionnerait ici. Assurez-vous que quelque part vous le relâchez à nouveau, sinon vous aurez une fuite de mémoire.

Souvent, les pointeurs intelligents aident avec ce genre de logique «ne pas oublier».

+1

+1 pour les pointeurs intelligents. Ne pas utiliser RAII pour ce genre de chose est probablement une mauvaise idée. – Brian

+1

Renvoyer une valeur serait presque certainement encore mieux. Même si l'objet est assez grand pour que la copie soit plus lente que l'allocation, la plupart des compilateurs éliront la copie de toute façon. –

+0

Donc le problème est que l'adresse retournée de ii ne signifie rien une fois qu'il est libéré de la fonction get_ii? –

2

Le problème dans votre code se produit parce que lorsque vous accédez à ii dans le principal, après get_ii() retourne, votre variable accessible par ii est déjà détruit.

Si vous retournez une mémoire allouée sur tas de get_ii, la mémoire sera accessible jusqu'à ce qu'il soit explicitement détruit

3

droit - mémoire du tas (alloué par malloc, par exemple) seront persistants en dehors de get_ii() portée. Assurez-vous juste de le libérer. Vous pouvez également allouer déclarer ii comme static int ii, auquel cas son pointeur existerait également en dehors de get_ii().

+2

Cependant, une des mises en garde est que le déclarer comme un 'static' alors le configure ainsi il y a une copie pour tous les appels à la fonction. C'est une décision de conception qui devrait être prise avec prévoyance et compréhension, et certainement pas la bonne chose à faire dans un programme multi-thread. – Omnifarious

+0

Il ne nécessite même pas de filetage pour être un problème. Si un point dans le programme obtient le pointeur et s'y accroche, un autre point pourrait faire la même chose et une modification de la valeur à un endroit affecterait l'autre. – KeithB

3

Vous renvoyez essentiellement l'adresse d'une variable qui n'existe plus (ii est créé lorsque la fonction get_ii() commence à s'exécuter et n'existe que jusqu'à la sortie de la fonction). Tout accès à la mémoire pointé par int *ii dans main() provoque un comportement indéfini. Par contre, la mémoire heap est allouée lorsque vous la demandez explicitement, et n'est pas libérée jusqu'à ce que vous la demandiez explicitement. Donc, si vous allouez un bloc de mémoire à l'intérieur d'une fonction, il est parfaitement possible de renvoyer le pointeur vers ce bloc de mémoire à l'appelant. Assurez-vous de documenter qui est responsable de libérer le bloc de mémoire quand il n'est plus nécessaire!

0

Si vous écrivez du code C, il vous suffit de passer un pointeur sur un objet et de modifier l'objet via ce pointeur. De cette façon, la fonction get_ii ne s'inquiète pas de savoir d'où vient l'objet. La fonction d'appel en prend soin. Si vous écrivez un style C++, vous devez retourner une valeur ou renvoyer un pointeur intelligent ou prendre une référence et modifier l'objet via cette référence. Ou vous pouvez utiliser le style C et passer un pointeur. Certains auteurs C++ préfèrent le passage de pointeur, car il indique clairement que l'objet est en cours de modification alors qu'une passe de référence n'est pas claire.

Maintenant, si l'objet est petit comme dans cet exemple, vous devriez toujours le passer et le renvoyer par valeur. Il est plus rapide et moins cher que l'utilisation d'un pointeur et rend le codage plus simple.

+0

Même si l'objet renvoyé est volumineux, l'optimisation de la valeur de retour peut permettre au compilateur de le construire à l'endroit où il est retourné, éliminant ainsi toute copie. – jcoder

+0

Si vous renvoyez une structure, l'ABI habituel passe en fait un pointeur sur une structure à remplir en tant que premier argument, ce qui le rend équivalent à la version pass-a-pointer. Il y a quelques différences dues à des exceptions potentielles (et éventuellement à des threads), mais si vous assignez à un local, il devrait faire la bonne chose (et pourrait mieux optimiser). –

2

encore plus néfaste:

std::string& makeString() //returns a reference 
{ std::string str = "Dustin"; return str; } 

main(){ 
std::string s = makeString(); 
//s is a dangling reference! makeString returns a reference to str, 
//but str is on the stack and goes out of scope, so we're keeping a reference to nothing 
} 

str (à l'intérieur makeString) est sur la pile et est détruit lorsque makeString rendements. résoudre cette erreur en retournant la valeur au lieu de by-reference, ce qui rend une copie de la droite avant qu'il ne soit hors de portée.

+0

Je pensais que les variables de pile étaient détruites? Deuxièmement, comment cette erreur est-elle résolue? –

+0

a mis à jour ma réponse –