web-dev-qa-db-fra.com

Comment accéder à une variable locale à partir d'une fonction différente à l'aide de pointeurs?

Puis-je avoir accès à une variable locale dans une fonction différente? Si c'est le cas, comment?

void replaceNumberAndPrint(int array[3]) {
    printf("%i\n", array[1]);
    printf("%i\n", array[1]);
}

int * getArray() {
    int myArray[3] = {4, 65, 23};
    return myArray;
}

int main() {
    replaceNumberAndPrint(getArray());
}

La sortie du morceau de code ci-dessus:

65
4202656

Qu'est-ce que je fais mal? Que signifie le "4202656"?

Dois-je copier tout le tableau dans la fonction replaceNumberAndPrint() pour pouvoir y accéder plus que la première fois?

48
Radek Simko

myArray est une variable locale et donc le pointeur n'est valide que jusqu'à la fin de sa portée (qui est dans ce cas la fonction contenant getArray). Si vous y accédez plus tard, vous obtenez un comportement indéfini.

En pratique, ce qui se passe est que l'appel à printf écrase la partie de la pile utilisée par myArray et qu'il contient ensuite d'autres données.

Pour corriger votre code, vous devez soit déclarer le tableau dans une étendue qui dure assez longtemps (la fonction main dans votre exemple), soit l'allouer sur le tas. Si vous l'allouez sur le tas, vous devez le libérer manuellement ou en C++ à l'aide de RAII.

Une alternative que j'ai ratée (probablement même la meilleure ici, à condition que le tableau ne soit pas trop grand) consiste à envelopper votre tableau dans une structure et à en faire un type de valeur. Le renvoyer ensuite crée une copie qui survit au retour de la fonction. Voir tp1réponse pour plus de détails à ce sujet.

56
CodesInChaos

Vous ne pouvez pas accéder à une variable locale une fois qu'elle est hors de portée. C'est ce que signifie être une variable locale.

Lorsque vous accédez au tableau dans la fonction replaceNumberAndPrint, le résultat n'est pas défini. Le fait qu'il semble fonctionner pour la première fois n'est qu'une heureuse coïncidence. L'emplacement de mémoire que vous pointez n'est probablement pas alloué sur la pile et est toujours correctement défini pour le premier appel, mais l'appel à printf l'écrase ensuite en poussant les valeurs sur la pile pendant son fonctionnement, c'est pourquoi le deuxième appel à printf affiche quelque chose différent.

Vous devez stocker les données du tableau sur le tas et passer un pointeur, ou dans une variable qui reste dans la portée (par exemple un global ou quelque chose de portée dans la fonction principale).

18
James Gaunt

Essayez quelque chose comme ça. La façon dont vous le faites "tue" myArray cause s'il est défini localement.

#include <stdio.h>
#include <stdlib.h>

void replaceNumberAndPrint(int * array) {
 printf("%i\n", array[0]);
 printf("%i\n", array[1]);
 printf("%i\n" , array[2]);
 free(array);
}

int * getArray() {
 int * myArray = malloc(sizeof(int) * 3);
 myArray[0] = 4;
 myArray[1] = 64;
 myArray[2] = 23;
 //{4, 65, 23};
 return myArray;
}

int main() {
 replaceNumberAndPrint(getArray());
}

Plus: http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/

Edit: Comme les commentaires l'ont correctement souligné: Une meilleure façon de procéder serait que:

#include <stdio.h>
#include <stdlib.h>

void replaceNumberAndPrint(int * array) {
    if(!array)
        return;

    printf("%i\n", array[0]);
    printf("%i\n", array[1]);
    printf("%i\n" , array[2]);
}

int * createArray() {
    int * myArray = malloc(sizeof(int) * 3);

    if(!myArray)
        return 0;

    myArray[0] = 4;
    myArray[1] = 64;
    myArray[2] = 23;
    return myArray;
}

int main() {
    int * array = createArray();
    if(array)
    {
        replaceNumberAndPrint(array);
        free(array);
    }
    return 0;
}
8
user418748

myArray devient hors de portée dès que vous quittez getArray. Vous devez plutôt lui allouer de l'espace sur le tas.

2
John Pickup

Votre code invoque un comportement indéfini car myArray sort de la portée dès que getArray() revient et que toute tentative de tiliser (déréférence) le pointeur pendant est UB.

2
Prasoon Saurav

Les variables locales sont hors de portée lors du retour, vous ne pouvez donc pas renvoyer de pointeur vers une variable locale.

Vous devez l'allouer dynamiquement (sur le tas), en utilisant malloc ou new. Exemple:

int *create_array(void) {
    int *array = malloc(3 * sizeof(int));
    assert(array != NULL);
    array[0] = 4;
    array[1] = 65;
    array[2] = 23;
    return array;
 }
 void destroy_array(int *array) {
     free(array);
 }
 int main(int argc, char **argv) {
     int *array = create_array();
     for (size_t i = 0; i < 3; ++i)
         printf("%d\n", array[i]);
     destroy_array(array);
     return 0;
 }

Alternativement, vous pouvez déclarer le tableau comme statique, en gardant à l'esprit que la sémantique est différente. Exemple:

int *get_array(void) {
    static int array[] = { 4, 65, 23 };
    return array;
 }
 int main(int argc, char **argv) {
     int *array = get_array();
     for (size_t i = 0; i < 3; ++i)
         printf("%d\n", array[i]);
     return 0;
 }

Si vous ne savez pas ce que signifie static, lisez cette question et réponse .

2
jweyrich

La bonne façon de procéder est la suivante:

struct Arr {
   int array[3];
};
Arr get_array() {
   Arr a;
   a.array[0] = 4;
   a.array[1] = 65;
   a.array[2] = 23;
   return a;
}
int main(int argc, char **argv) {
   Arr a = get_array();
   for(size_t i=0; i<3; i++)
       printf("%d\n", a.array[i]);
   return 0;
}

Pour comprendre pourquoi vous devez le faire, vous devez savoir comment fonctionne sizeof (array). C (et donc c ++) s'efforce d'éviter de copier le tableau, et vous avez besoin de la structure pour aller au-delà. La raison pour laquelle la copie est nécessaire est à cause des étendues - l'étendue de la fonction get_array () disparaît et chaque valeur encore nécessaire de cette étendue devra être copiée dans l'étendue appelante.

2
tp1

Solution C++:

"Puis-je avoir accès à une variable locale dans une fonction différente? Si oui, comment?"

La réponse est non, pas après la fin de la fonction. Les variables locales sont détruites à ce stade.

Dans C++ la façon de traiter les tableaux retournés est de les gérer dans un conteneur comme un std :: array (taille fixe) ou un std: : vecteur (taille dynamique).

Par exemple:

void replaceNumberAndPrint(const std::array<int, 3>& array) {
    printf("%i\n", array[0]);
    printf("%i\n", array[1]);
    printf("%i\n", array[2]);
}

std::array<int, 3> getArray() {
    std::array<int, 3> myArray = {4, 65, 23};
    return myArray;
}

Dans la deuxième fonction, la valeur retournée est optimisée par le compilateur afin que vous ne payiez pas le prix de la copie effective du tableau.

1
Galik

Dans ce code, vous avez utilisé un pointeur sur des objets locaux, mais lorsqu'une fonction renvoie toutes les variables locales sortent du domaine. Si vous allouez de la mémoire (en utilisant la fonction malloc() pour l'allocation), aucune donnée ne sera perdue ou écrasée.

int* getArray(int size) {
    int *myArray = (int*)malloc(size*sizeof(int));
    myArray[0] = 4;
    myArray[1] = 65;
    myArray[2] = 23;
    return myArray;
}

int main() {
    int i;
    int *vector = getArray(3);
    for(i=0;i<3;i++)
    {
        printf("%i\n",vector[i]);
    }
    getch();
    return 0;
}

Ce code imprimera tous les éléments du tableau et aucun écrasement ne se produira.

0
aquib