fout: functie retourneert adres van lokale variabele

Ik ben een beginner met C en ik leer alleen.
Ik ben de volgende functie aan het maken:

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 ...
}

Ik probeer in feite een toegevoegde tekenreeks te retourneren, maar ik krijg de volgende foutmelding:

“fout: functie retourneert adres van lokale variabele”, suggesties, hoe dit op te lossen?


Antwoord 1, autoriteit 100%

De lokale variabelen hebben een levensduur die zich alleen uitstrekt binnen het blok waarin ze zijn gedefinieerd. Op het moment dat de besturing buiten het blok komt waarin de lokale variabele is gedefinieerd, wordt de opslag voor de variabele niet meer toegewezen (niet gegarandeerd). Daarom zal het gebruik van het geheugenadres van de variabele buiten het levensduurgebied van de variabele ongedefinieerd gedrag zijn.

Aan de andere kant kun je het volgende doen.

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

En gebruik in plaats daarvan de str_to_ret. En wanneer returning str_to_ret, wordt het adres dat is toegewezen door mallocgeretourneerd. Het geheugen toegewezen door mallocwordt toegewezen vanaf de heap, die een levensduur heeft die de volledige uitvoering van het programma beslaat. Daarom hebt u toegang tot de geheugenlocatie vanuit elk blok en op elk moment terwijl het programma draait.

Houd er ook rekening mee dat het een goede gewoonte is om, nadat u klaar bent met het toegewezen geheugenblok, het freete geven om geheugenlekken te voorkomen. Zodra je het geheugen hebt vrijgemaakt, heb je geen toegang meer tot dat blok.


Antwoord 2, autoriteit 28%

Ik kwam met dit eenvoudige en duidelijke (ik hoop het) codevoorbeeld dat zichzelf zou moeten verklaren!

#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
}

Bij het compileren krijg je de [bedoelde] waarschuwing:

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? :)
            ^~~~~~

…wat we hier eigenlijk bespreken!

het uitvoeren van mijn voorbeeld levert deze uitvoer op:

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 

Theorie:

Dit is heel aardig beantwoord door gebruiker @phoxis.
Denk er in principe zo over: alles tussen {en }is lokalescope, dus volgens de C-Standard is het “undefined” daarbuiten.
Door malloc te gebruiken haal je het geheugen uit de HEAP(programmaomvang) en niet uit de STACK(functieomvang) – dus het is ‘zichtbaar’ van buitenaf.
De tweede juiste manier om dit te doen is call-by-reference. Hier definieer je de var binnen de parent-scope, dus het gebruikt de STACK (omdat de parent scope de main()is).

Samenvatting:

3 manieren om het te doen, een ervan is niet waar. C is nogal onhandig om alleen een functie een string met dynamische grootte te laten retourneren. Of je moet mallocen en het dan vrijgeven, of je moet per referentie bellen. Of gebruik C++ 😉


Antwoord 3, autoriteit 14%

Noch malloc of call by reference zijn nodig. U kunt een aanwijzer binnen de functie declareren en deze instellen op de tekenreeks/array die u wilt retourneren.

De code van @Gewure als basis gebruiken:

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; 
}

werkt perfect.

Met een non-loop versie van de code in de oorspronkelijke vraag:

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

Antwoord 4, autoriteit 5%

ais een array die lokaal is voor de functie. Zodra de functie retourneert, bestaat deze niet meer en daarom moet u het adres van een lokale variabele niet retourneren.
Met andere woorden, de levensduurvan avalt binnen het bereik({,}) van de functie en als je er een aanwijzer naar terugstuurt, is wat je hebt een aanwijzer die verwijst naar een geheugen dat niet geldig is. Dergelijke variabelen worden ook automatische-variabelen genoemd, omdat hun levensduur automatisch wordt beheerd, u hoeft dit niet expliciet te beheren.

Omdat je de variabele moet uitbreiden om buiten het bereik van de functie te blijven, moet je een array op heap toewijzen en er een aanwijzer naar terugsturen.

char *a = malloc(1000); 

Op deze manier blijft de array ain het geheugen totdat je een free()op hetzelfde adres aanroept.
Vergeet dit niet te doen, anders krijg je een geheugenlek.


Antwoord 5, autoriteit 5%

Deze regel:

char b = "blah"; 

Is niet goed – uw waarde moet een aanwijzer zijn.

Uw code loopt ook het risico van een stack overflow, aangezien uw recursiecontrole de afnemende waarde van x niet begrenst.

Hoe dan ook, de eigenlijke foutmelding die je krijgt is omdat char aeen automatische variabele is; op het moment dat je returnzal het ophouden te bestaan. Je hebt iets anders nodig dan een automatische variabele.


Antwoord 6, autoriteit 2%

awordt lokaal in de functie gedefinieerd en kan niet buiten de functie worden gebruikt. Als u een char-array van de functie wilt retourneren, moet u deze dynamisch toewijzen:

char *a = malloc(1000); 

En bel op een gegeven moment freeop de geretourneerde aanwijzer.

Je zou ook een waarschuwing moeten zien op deze regel: char b = "blah";: je probeert een letterlijke tekenreeks toe te wijzen aan een char.


Antwoord 7

char b = "blah"; 

zou moeten zijn:

char *b = "blah"; 

Antwoord 8

Alle antwoorden leggen het probleem heel goed uit.

Ik wil echter nog andere informatie toevoegen.

Ik had hetzelfde probleem op het moment dat ik de uitvoer van a . wilde hebben
functie om een vector te zijn.

In deze situatie is de gebruikelijke oplossing om de uitvoer als een argument van de functie zelf te declareren. Op deze manier worden de allocvan de variabele en de fysieke ruimte die nodig is om de informatie op te slaan buiten de functie beheerd. Pseudocode om de klassieke oplossing uit te leggen is:

void function(int input, int* output){
    //...
    output[0] = something;
    output[1] = somethig_else;
    //...
    return;
}

In dit geval moet de voorbeeldcode in de vraag worden gewijzigd in:

void foo(int x, char* a){
     if(x < 0){
        char b = "blah";
        //...
        strcpy(a, b);
        //..
        return;
      }
    //..
}

Other episodes