2010-06-16 9 views
0

Prenons l'exemple suivant:C++: Trouble with Pointeurs, variables de boucle, et struct

#include <iostream> 
#include <sstream> 
#include <vector> 
#include <wchar.h> 
#include <stdlib.h> 

using namespace std; 

struct odp { 
    int f; 
    wchar_t* pstr; 
}; 

int main() 
{ 
    vector<odp> vec; 
    ostringstream ss; 

    wchar_t base[5]; 
    wcscpy_s(base, L"1234"); 

    for (int i = 0; i < 4; i++) 
    { 
     odp foo; 
     foo.f = i; 

     wchar_t loopStr[1]; 
     foo.pstr = loopStr; // wchar_t* = wchar_t ? Why does this work? 
     foo.pstr[0] = base[i]; 

     vec.push_back(foo); 
    } 

    for (vector<odp>::iterator iter = vec.begin(); iter != vec.end(); iter++) 
    { 
     cout << "Vec contains: " << iter->f << ", " << *(iter->pstr) << endl; 
    } 
} 

Ce produit:

Vec contains: 0, 52 
Vec contains: 1, 52 
Vec contains: 2, 52 
Vec contains: 3, 52 

J'espère que chaque fois, iter->f et iter->pstr donnerait un autre résultat. Malheureusement, iter->pstr est toujours le même.

Mon soupçon est que chaque fois à travers la boucle, un nouveau loopStr est créé. Au lieu de le copier dans la structure, je ne fais que copier un pointeur. L'emplacement sur lequel le pointeur écrit est écrasé.

Comment puis-je éviter cela? Est-il possible de résoudre ce problème sans allouer de la mémoire sur le tas?

Répondre

2
... 
    odp foo; 
    foo.f = i; 

    wchar_t loopStr[1]; //A 
    foo.pstr = loopStr; //B 
    foo.pstr[0] = base[i]; //C 

    vec.push_back(foo); 
    ... 

A - Vous allouer un tableau (de taille 1) de la pile

B - Affectez foo.pstr pour pointer vers le tableau sur la pile

C - Affectez [base i] au premier élément du tableau (qui est sur la pile)

Après que la boucle for a quitté son cycle actuel, la variable loopStr n'est plus dans la portée et son contenu est indéfini. L'itération de la boucle suivante réutilisera probablement la même adresse de mémoire (d'où la même valeur lorsque vous imprimez à la fin). Si vous avez des optimisations activées, votre compilateur C peut vous avertir de prendre des adresses de variables locales (bien que j'en doute).

sans utiliser l'allocation de tas, je pense que votre seule option est de fixer la taille de foo.pstr ODP, à savoir

struct odp { 
    int f; 
    wchar_t pstr[1]; 
}; 

ou allouer le tableau sur le tas dans le cadre de l'initialisation du odp

Mieux encore utiliser std :: wstring puisque vous utilisez C++, et laissez-le faire l'allocation et la gestion de la mémoire pour vous.

2

Il semblerait que pstr pointe sur la variable de portée locale loopStr, de sorte que les résultats ne sont pas définis (il semble y avoir toujours la dernière valeur stockée lors de l'impression des résultats). Si vous avez imprimé l'adresse de pstr dans la boucle, je crois que ce serait la même chose pour chaque position dans la boucle.

4

Ce que vous avez ici est un comportement indéfini. Chaque fois que vous bouclez votre boucle, vous créez et détruisez un tableau, puis assignez son adresse à foo.pstr et repoussez-la dans votre vecteur. Le compilateur arrive juste à créer ce tableau au même endroit à chaque fois (ce qui est logique mais pas nécessaire). Lorsque vous imprimez, vous imprimez techniquement des données supprimées, c'est juste que le système n'est pas une bitch qui vous tape dessus parce que ce n'est pas de l'espace protégé. L'emplacement de mémoire a simplement tout ce qui lui a été assigné en dernier.

Vous résolvez cela en interrompant l'utilisation de pointeurs et de tableaux de caractères bruts.

2
foo.pstr = loopStr; // wchar_t* = wchar_t ? Why does this work? 

Cela ne fonctionne pas, du moins pas comme vous le souhaitez. loopStr est un tableau, mais vous pouvez aussi l'utiliser comme un pointeur.Donc, lorsque vous affectez au pointeur foo.pstr, il obtient l'adresse du premier élément dans loopStr. Cela se trouve être une variable locale allouée sur la pile et n'est valide que dans la boucle for.