6
votes

changement de valeur de pointeur C ++

Je suis aussi relativement nouveau en programmation C ++. Pendant que je travaillais sur un code sur le passage d'arguments avec un tableau de pointeurs de caractères. J'ai rencontré un problème où la valeur de mes pointeurs est modifiée après certaines opérations. Ci-dessous mon code.

#include <iostream>
using namespace std;

void input(char* argv[], int &i)
{
    char buff[10][20]; //buffer string array
    while (cin.peek() != '\n') {
        cin >> buff[i++];
    }
    for (int j = 0; j < i; j++) {
        argv[j] = buff[j];
    }
    argv[i] = NULL; // putting a NULL at the end 
}

int main(int argc, char* argv[])
{
    char *arg[10];
    int i = 0;
    input(arg, i); //input the arguments

    for (int j = 0; j < i; j++) {
        cout << arg[j] << endl;  //output the arguments entered
    }

    return 0;
}

La sous-fonction void input (char * argv [], int & i) est censée me permettre de saisir mes arguments jusqu'à 9 fois ou quand un la touche Entrée est enfoncée. Tandis que i indique le nombre total d'arguments.

Les arguments sont ensuite stockés sous forme de tableau de pointeurs de caractères, puis les retransmettent au char * arg [10] de la fonction principale pour les maintenir.

Cependant, j'ai trouvé qu'après cout Les valeurs de arg sont perdues et des valeurs aléatoires sont imprimées.


3 commentaires

Utilisez std :: string (si possible) pour votre propre santé mentale.


Dans la fonction input , la variable buff est une variable locale. Sa durée de vie se termine lorsque la fonction se termine, et en quelque sorte cesse d'exister. Tous les pointeurs que vous enregistrez sur cette variable seront des pointeurs parasites et ne pourront pas être déréférencés.


Outre la durée de vie du problème buff , vous aurez également un problème si une entrée de plus de 19 caractères (sans espace) est rencontrée car opérateur >> avec un droit L'argument côté main de type char * écrasera alors la mémoire suivante. Cela peut être évité en définissant la width () du flux de manière appropriée, par exemple cin >> std :: setw (20) avant chaque entrée dans buff . Cependant, l'utilisation de std :: string est toujours considérée comme meilleure. (À partir de C ++ 20, la bibliothèque pourra utiliser la taille déduite du tableau char - une chose en moins à craindre.)


3 Réponses :


11
votes

Vous créez un tableau bidimensionnel de caractères buff sur la pile, puis vous renvoyez des pointeurs dans ce tableau via le paramètre argv . Mais buff vit sur la pile et cesse d'exister dès que la fonction input se termine. La mémoire utilisée par buff sera écrasée par d'autres fonctions que vous appelez après avoir appelé input .

Vous devez allouer buff dans main , puis le passer dans input afin qu'il continue à vivre dans la portée de main < / code> après le retour de input .

Une autre option serait d'allouer de l'espace de tas pour buff dans input . Dans ce cas, la fonction main serait responsable de la libération de la mémoire après l'avoir utilisée.

De toute évidence, vous pouvez utiliser des fonctionnalités C ++ plus avancées pour éviter une partie de cette surcharge. Bien qu'il s'agisse d'un programme C ++, il est effectivement écrit en C. Mais comprendre le fonctionnement de la mémoire et des pointeurs est essentiel pour comprendre les problèmes que les nouvelles fonctionnalités C ++ résolvent.


3 commentaires

Je ne comprends pas très bien. Vous avez dit: "buff cesse d'exister dès que la fonction d'entrée existe" D'après ma compréhension, la variable buff vit jusqu'au retour de la fonction d'entrée?


Oui, et une fois la fonction d'entrée retournée, vous disposez d'un tableau de pointeurs dans un tampon qui n'existe plus et dont le stockage peut être écrasé à tout moment. Cette réponse utilise «sorties» pour signifier la même chose que «retourne», c'est-à-dire, contrôle quitte la fonction d'entrée et renvoie à l'appelant.


@ASMJunkie J'ai écrit que buff cesse d'exister dès que la fonction d'entrée se termine (n'existe pas). Peut-être un mauvais choix de mots. :-)



1
votes

la valeur de mes pointeurs est modifiée

Les pointeurs sont les seules choses qui n'ont pas été endommagées. Le problème est la mémoire qu'ils pointent vers .

Vous pouvez prouver la première partie en imprimant la valeur de chacun de ces pointeurs, ou simplement en les inspectant dans le débogueur. (Vous pouvez imprimer l'adresse plutôt que la chaîne C vers laquelle elle pointe en convertissant en void, comme cout << static_cast (arg [j]) << '\ n' ).

Alors, qu'est-il arrivé à vos chaînes C? Eh bien, vous avez déclaré une variable de tableau à portée automatique dans la fonction input . Ce tableau cesse d'exister lorsque la fonction se termine, comme toute autre variable à portée automatique. Accéder à la mémoire où vivait une variable, après que la variable a cessé d'exister, est illégal.

Le fait que vous ayez renvoyé des pointeurs dans ce tableau ne rend pas légal de les lire (déréférencer) après que le tableau lui-même soit hors de portée, et il s'agit en fait d'un comportement indéfini.

Le contenu en cours d'écrasement est en fait le meilleur cas, car cela signifie que vous avez remarqué le bogue: il aurait pu légalement planter ou, pire encore, sembler fonctionner parfaitement jusqu'à ce que vous ayez soumis / déployé / vendu le programme, et s'est écrasé à chaque exécution par la suite.


0 commentaires

0
votes

Pensez à la pile comme étant une grande quantité de mémoire (mais pas illimitée). Il est alloué et libéré simplement en déplaçant le pointeur de pile vers le bas et vers le haut (les directions dépendront du matériel).

Voici votre code avec quelques annotations.

input(arg, i);
// when you get here the stack pointer will have been moved up, freeing the space
// that was allocated for 'buf' in 'input'

// the space for 'j' could overwrite the space where 'buf' was
for (int j = 0; j < i; j++) {
    // the calls to 'cout' and 'end;' could overwrite the space where 'buf was'
    cout << arg[j] << endl;
}


0 commentaires