Вопрос по strcpy, return, strcat, c – ошибка: функция возвращает адрес локальной переменной

17

Я начинаю с 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 ...
}

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

& quot; ошибка: функция возвращает адрес локальной переменной & quot ;, есть предложения, как это исправить?

Когда вы пишете свой вопрос, он предлагает несколько дубликатов на его основе. Вы, вероятно, должны были проверить это. netcoder
я думаю, что это может быть полезноstackoverflow.com/a/6897993 Luiz Cajueiro
возможный дубликатC Warning: Function returns address of local variable netcoder
char b = "blah"; не является действительным C-декаратом. Также дляb объявлен какchar strcpy(a, b) тоже недействительно. Вы публикуете реальный код? AnT

Ваш Ответ

7   ответов
-2
char b = "blah"; 

должно быть:

char *b = "blah"; 
@ Вы правы, что странно! :) довольно хакерский и скрытый. Джерри должен объяснить это в своем ответе!
это не будет иметь значения. Прочитайте мой ответ почему!
@Gewure: & quot;this won't make a difference& Quot; как строковые литералыnot жить в стеке функции, но"remain alive throughout the program lifetime. They have static duration lifetime."
34

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

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

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

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

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

Просто пример, чтобы объяснить время жизни объекта.
Sitenote: Инкапсуляция / срок службы / ответственность. Недостаток дизайна в решении: CALLER инициализирует Malloc - но CALLED должен его освободить. Кроме того, если вы не выполняете санитарную обработку / проверку значений, данных для функции, вы можете легко распределить неимоверно большой блок в куче ...
2

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

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

char *a = malloc(1000); 

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

1

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

char *a = malloc(1000);

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

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

5

Ни 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;
}
@Shyri хм, глубокий вопрос, особенно вторая часть. Мне тоже интересно сейчас. то, что вы спрашиваете, в основном, как куча убедиться, что указатели на нем не повреждены ..?
спасибо за кредиты :)
Я всего лишь новичок в. Согласно предыдущим ответам: использование вызова по ссылке будет использовать переменную, объявленную в области действия вызывающих абонентов, поэтому она сохраняется, а использование malloc должно освободить эту память после этого, чтобы избежать утечек памяти. Как это применимо здесь? Указатель создается в области видимости вызываемой функции. Как мы узнаем, что область памяти, к которой обращается этот указатель, остается нетронутой, когда мы хотим ее прочитать? Спасибо
3

Эта строка:

char b = "blah";

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

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

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

7

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

#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 

Theory:

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

Summary:

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

Похожие вопросы