web-dev-qa-db-fra.com

erreur: la fonction renvoie l'adresse de la variable locale

Je suis débutant avec C et j'apprends par moi-même. Je crée la fonction suivante:

char *foo(int x){
     if(x < 0){
        char a[1000];
        char b = "blah";
        x = x - 1;
        char *c = foo(x);
        strcpy(a, b);
        strcat(a, c);
        return a;
      }
    blah ...
}

J'essaie essentiellement de renvoyer une chaîne ajoutée, mais j'obtiens l'erreur suivante:

"erreur: la fonction renvoie l'adresse de la variable locale", des suggestions, comment y remédier?

23
Hello World

Les variables locales ont une durée de vie qui ne s'étend qu'à l'intérieur du bloc dans lequel elle est définie. Au moment où le contrôle sort du bloc dans lequel la variable locale est définie, le stockage de la variable n'est plus alloué (non garanti). Par conséquent, l'utilisation de l'adresse mémoire de la variable en dehors de la zone de durée de vie de la variable sera un comportement non défini.

D'un autre côté, vous pouvez effectuer les opérations suivantes.

 char *str_to_ret = malloc (sizeof (char) * required_size);
  .
  .
  .
 return str_to_ret;

Et utilisez le str_to_ret au lieu. Et quand returning str_to_ret, l'adresse allouée par malloc sera retournée. La mémoire allouée par malloc est allouée à partir du tas, qui a une durée de vie qui s'étend sur toute l'exécution du programme. Par conséquent, vous pouvez accéder à l'emplacement de la mémoire à partir de n'importe quel bloc et à tout moment pendant l'exécution du programme.

Notez également que c'est une bonne pratique qu'après avoir terminé avec le bloc de mémoire alloué, free de le sauver des fuites de mémoire. Une fois que vous avez libéré la mémoire, vous ne pouvez plus accéder à ce bloc.

41
phoxis

J'ai trouvé cet exemple de code simple et direct (je l'espère) qui devrait s'expliquer!

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

/* function header definitions */
char* getString();                     //<- with malloc (good practice)
char * getStringNoMalloc();  //<- without malloc (fails! don't do this!)
void getStringCallByRef(char* reference); //<- callbyref (good practice)

/* the main */
int main(int argc, char*argv[]) {

    //######### calling with malloc
    char * a = getString();
    printf("MALLOC ### a = %s \n", a); 
    free(a);

    //######### calling without malloc
    char * b = getStringNoMalloc();
    printf("NO MALLOC ### b = %s \n", b); //this doesnt work, question to yourself: WHY?
    //HINT: the warning says that a local reference is returned. ??!
    //NO free here!

    //######### call-by-reference
    char c[100];
    getStringCallByRef(c);
    printf("CALLBYREF ### c = %s \n", c);

    return 0;
}

//WITH malloc
char* getString() {

    char * string;
    string = malloc(sizeof(char)*100);

    strcat(string, "bla");
    strcat(string, "/");
    strcat(string, "blub");

    printf("string : '%s'\n", string);

    return string;
}

//WITHOUT malloc (watch how it does not work this time)
char* getStringNoMalloc() {

     char string[100] = {};

     strcat(string, "bla");
     strcat(string, "/");
     strcat(string, "blub");
     //INSIDE this function "string" is OK
     printf("string : '%s'\n", string);

     return string; //but after returning.. it is NULL? :)
}

// ..and the call-by-reference way to do it (prefered)
void getStringCallByRef(char* reference) {

    strcat(reference, "bla");
    strcat(reference, "/");
    strcat(reference, "blub");
    //INSIDE this function "string" is OK
    printf("string : '%s'\n", reference);
    //OUTSIDE it is also OK because we hand over a reference defined in MAIN
    // and not defined in this scope (local), which is destroyed after the function finished
}

Lors de sa compilation, vous obtenez l'avertissement [prévu]:

me@box:~$ gcc -o example.o example.c 
example.c: In function ‘getStringNoMalloc’:
example.c:58:16: warning: function returns address of local variable [-Wreturn-local-addr]
         return string; //but after returning.. it is NULL? :)
            ^~~~~~

... essentiellement ce dont nous discutons ici!

l'exécution de mon exemple donne cette sortie:

me@box:~$ ./example.o 
string : 'bla/blub'
MALLOC ### a = bla/blub 
string : 'bla/blub'
NO MALLOC ### b = (null) 
string : 'bla/blub'
CALLBYREF ### c = bla/blub 

Théorie:

Cela a été très bien répondu par l'utilisateur @phoxis. Fondamentalement, pensez-y de cette façon: tout ce qui se trouve entre { et } est local portée, donc par la norme C n'est pas définie à l'extérieur. En utilisant malloc, vous prenez la mémoire du [~ # ~] tas [~ # ~] (portée du programme) et non du [~ # ~] pile [~ # ~] (portée de la fonction) - donc son 'visible' de l'extérieur. La deuxième façon correcte de le faire est l'appel par référence . Ici, vous définissez le var à l'intérieur de la portée parent, donc il utilise la pile (car la portée parent est le main ()).

Résumé:

3 façons de le faire, l'un d'eux est faux. C est un peu maladroit d'avoir juste une fonction qui retourne une chaîne de taille dynamique. Soit vous devez malloc puis le libérer, soit vous devez appeler par référence. Ou utilisez C++;)

9
Gewure

Ni malloc ni appel par référence ne sont nécessaires. Vous pouvez déclarer un pointeur dans la fonction et le définir sur la chaîne/le tableau que vous souhaitez retourner.

En utilisant le code de @ Gewure comme base:

char *getStringNoMalloc(void){
    char string[100] = {};
    char *s_ptr = string;

    strcat(string, "bla");
    strcat(string, "/");
    strcat(string, "blub");
    //INSIDE this function "string" is OK
    printf("string : '%s'\n", string);

    return s_ptr; 
}

fonctionne parfaitement.

Avec une version sans boucle du code dans la question d'origine:

char *foo(int x){    
    char a[1000];
    char *a_ptr = a;
    char *b = "blah";       

    strcpy(a, b);

    return a_ptr;
}
7
AuContraire

Cette ligne:

char b = "blah";

N'est pas bon - votre valeur l doit être un pointeur.

Votre code est également en danger de débordement de pile, car votre vérification de récursivité ne limite pas la valeur décroissante de x.

Quoi qu'il en soit, le message d'erreur réel que vous obtenez est dû au fait que char a est une variable automatique; dès que vous return elle cessera d'exister. Vous avez besoin d'autre chose qu'une variable automatique.

3
Brent Arias

a est un tableau local à la fonction. Une fois que la fonction retourne, elle n'existe plus et vous ne devez donc pas renvoyer l'adresse d'une variable locale.
En d'autres termes, la durée de vie de a est dans la portée ({, }) De la fonction et si vous lui renvoyez un pointeur, vous disposez d'un pointeur pointant vers une mémoire non valide. De telles variables sont également appelées ( automatic variabels car leur durée de vie est gérée automatiquement, vous n'avez pas besoin de la gérer explicitement.

Puisque vous devez étendre la variable pour qu'elle persiste au-delà de la portée de la fonction, vous devez allouer un tableau sur le tas et lui renvoyer un pointeur.

char *a = malloc(1000); 

De cette façon, le tableau a réside en mémoire jusqu'à ce que vous appeliez une free() sur la même adresse.
N'oubliez pas de le faire ou vous vous retrouvez avec une fuite de mémoire.

2
Alok Save

a est défini localement dans la fonction et ne peut pas être utilisé en dehors de la fonction. Si vous souhaitez renvoyer un tableau char à partir de la fonction, vous devrez l'allouer dynamiquement:

char *a = malloc(1000);

Et à un moment donné, appelez free sur le pointeur renvoyé.

Vous devriez également voir un avertissement sur cette ligne: char b = "blah";: vous essayez d'assigner un littéral de chaîne à un char.

1
pb2q