Подтвердить что ты не робот

ошибка: функция возвращает адрес локальной переменной

Я начинаю с C, и сам изучаю сам. Я создаю следующую функцию:

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

В основном я пытаюсь вернуть добавленную строку, но я получаю следующую ошибку:

"error: функция возвращает адрес локальной переменной", любые предложения, как исправить это?

4b9b3361

Ответ 1

Локальные переменные имеют время жизни, которое распространяется только внутри блока, в котором они определены. В тот момент, когда элемент управления выходит за пределы блока, в котором определена локальная переменная, память для переменной больше не выделяется (не гарантируется). Следовательно, использование адреса памяти переменной вне области времени жизни переменной будет неопределенным поведением.

С другой стороны, вы можете сделать следующее.

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

И используйте вместо этого str_to_ret. И когда return str_to_ret, адрес, выделенный malloc будет возвращен. Память, выделенная malloc, выделяется из кучи, срок действия которой охватывает весь процесс выполнения программы. Таким образом, вы можете получить доступ к ячейке памяти из любого блока и в любое время во время работы программы.

Также обратите внимание, что это хорошая практика, что после того, как вы сделали с выделенным блоком памяти, free его, чтобы сохранить от утечек памяти. Как только вы освободите память, вы не сможете снова получить доступ к этому блоку.

Ответ 2

Я придумал этот простой и прямой (я надеюсь, что) пример кода, который должен объяснить сам!

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

При его компиляции вы получаете [предназначенное] предупреждение:

[email protected]:~$ 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? :)
            ^~~~~~

... в основном то, что мы обсуждаем здесь!

Выполнение моего примера дает такой вывод:

[email protected]:~$ ./example.o 
string : 'bla/blub'
MALLOC ### a = bla/blub 
string : 'bla/blub'
NO MALLOC ### b = (null) 
string : 'bla/blub'
CALLBYREF ### c = bla/blub 

Теория:

На это очень хорошо ответил Пользователь @phoxis. В основном, думайте об этом следующим образом: все, что между { и } является локальной областью действия, таким образом, согласно стандарту C, является "неопределенным" снаружи. Используя malloc, вы берете память из HEAP (область действия программы), а не из STACK (область действия функции) - таким образом, она "видна" извне. Второй правильный способ сделать это - вызов по ссылке. Здесь вы определяете переменную внутри родительской области, поэтому она использует STACK (потому что родительская область - это main()).

Резюме:

3 способа сделать это, один из них ложный. C немного неуклюже, просто чтобы функция возвращала строку динамического размера. Либо вы должны выполнить malloc, а затем освободить его, либо вы должны обратиться по ссылке. Или используйте C++;)

Ответ 3

Ни malloc, ни вызов по ссылке не нужны. Вы можете объявить указатель внутри функции и установить его в строку/массив, который вы хотите вернуть.

Использование в качестве основы кода @Gewure:

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

отлично работает.

С не-петлевой версией кода в исходном вопросе:

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

    strcpy(a, b);

    return a_ptr;
}

Ответ 4

Эта строка:

char b = "blah";

Нехорошо - ваша lvalue должна быть указателем.

Ваш код также подвержен переполнению стека, так как ваша проверка рекурсии не ограничивает уменьшающееся значение x.

В любом случае, фактическое сообщение об ошибке, которое вы получаете, состоит в том, что char a является автоматической переменной; в тот момент, когда вы return, он перестанет существовать. Вам нужно что-то другое, кроме автоматической переменной.

Ответ 5

a - это массив, локальный для функции. Когда функция возвращает ее, она больше не существует и, следовательно, вы не должны возвращать адрес локальной переменной.
Другими словами, продолжительность жизни a находится в пределах области ({, }) функции, и если вы вернете указатель на нее, то у вас есть указатель, указывающий на некоторую память что недопустимо. Такие переменные также называются автоматическими переменными, потому что их время жизни автоматически управляется, вам не нужно явно управлять им.

Поскольку вам нужно расширить эту переменную, чтобы она оставалась недоступной для функции, вам нужно выделить массив в куче и вернуть указатель на него.

char *a = malloc(1000); 

Таким образом, массив a находится в памяти до тех пор, пока вы не назовете free() на тот же адрес.
Не забудьте сделать это, или вы закончите утечку памяти.

Ответ 6

a определяется локально в функции и не может использоваться вне функции. Если вы хотите вернуть массив char из функции, вам нужно будет выделить его динамически:

char *a = malloc(1000);

И в какой-то момент вызовите free в возвращаемом указателе.

Вы также должны увидеть предупреждение в этой строке: char b = "blah";: вы пытаетесь присвоить строковый литерал char.

Ответ 7

char b = "blah"; 

должен быть:

char *b = "blah";