49

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

我基本上是在尝试返回附加的字符串,但出现以下错误:

“错误:函数返回局部变量的地址”,任何建议,如何解决这个问题?

4

8 回答 8

68

局部变量的生命周期仅在定义它的块内扩展。当控件超出定义局部变量的块时,不再分配变量的存储空间(不保证)。因此,在变量的生命周期之外使用变量的内存地址将是未定义的行为。

另一方面,您可以执行以下操作。

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

str_to_ret改用。并且在returning时,将返回str_to_ret分配的地址。malloc分配的内存malloc是从堆中分配的,堆的生命周期跨越程序的整个执行。因此,您可以在程序运行时从任何块和任何时间访问内存位置。

另请注意,在完成分配的内存块后,它是一种很好的做法,free以防止内存泄漏。释放内存后,您将无法再次访问该块。

于 2012-09-12T03:23:34.313 回答
19

我想出了这个简单直接(我希望如此)的代码示例,它应该可以自我解释!

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

编译它时,您会收到 [intended] 警告:

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

...基本上我们在这里讨论的内容!

运行我的示例会产生以下输出:

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 

理论:

用户@phoxis 已经很好地回答了这个问题。基本上这样想:{}之间的所有内容都是本地范围,因此 C-Standard 在外部是“未定义的”。通过使用 malloc,您可以从HEAP(程序范围)而不是从STACK(函数范围)获取内存 - 因此它从外部“可见”。第二种正确的方法是call-by-reference。在这里,您在父范围内定义 var,因此它使用 STACK(因为父范围是main())。

概括:

3 种方法,其中一种是错误的。C 有点笨拙,只是让一个函数返回一个动态大小的字符串。要么你必须 malloc 然后释放它,要么你必须通过引用调用。或者使用 C++ ;)

于 2017-02-22T13:06:35.293 回答
9

不需要 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;
}
于 2017-09-20T17:25:24.410 回答
3

a是函数的本地数组。一旦函数返回,它就不再存在,因此您不应该返回局部变量的地址。
换句话说,它的生命周期在函数a的作用域({})内,如果你返回一个指向它的指针,你所拥有的是一个指向一些无效内存的指针。此类变量也称为自动变量,因为它们的生命周期是自动管理的,您无需显式管理它。

由于您需要扩展变量以使其超出函数的范围,因此您需要在堆上分配一个数组并返回一个指向它的指针。

char *a = malloc(1000); 

这样,数组a驻留在内存中,直到您free()在同一地址上调用 a 。
不要忘记这样做,否则最终会导致内存泄漏。

于 2012-09-12T03:23:13.490 回答
3

这一行:

char b = "blah";

不好 - 你的左值需要是一个指针。

您的代码也有堆栈溢出的危险,因为您的递归检查没有限制 x 的递减值。

无论如何,您收到的实际错误消息是因为char a是一个自动变量;你的那一刻return,它将不复存在。您需要的不是自动变量。

于 2012-09-12T03:24:13.540 回答
1

a在函数中本地定义,不能在函数外使用。如果你想char从函数返回一个数组,你需要动态分配它:

char *a = malloc(1000);

并在某个时候调用free返回的指针。

您还应该在此行看到警告char b = "blah";::您正在尝试将字符串文字分配给char.

于 2012-09-12T03:22:19.513 回答
0
char b = "blah"; 

应该:

char *b = "blah"; 
于 2016-08-08T23:30:08.373 回答
0

所有的答案都很好地解释了这个问题。

但是,我想添加另一个信息。

当我希望函数的输出成为向量时,我遇到了同样的问题。

在这种情况下,常见的解决方案是将输出声明为函数本身的参数。这样,alloc存储信息所需的变量和物理空间在函数外部进行管理。解释经典解决方案的伪代码是:

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

在这种情况下,问题中的示例代码应更改为:

void foo(int x, char* a){
     if(x < 0){
        char b = "blah";
        //...
        strcpy(a, b);
        //..
        return;
      }
    //..
}
于 2020-02-26T09:20:41.807 回答