【问题标题】:error: function returns address of local variable错误:函数返回局部变量的地址
【发布时间】:2012-09-05 00:51:14
【问题描述】:

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

我基本上是在尝试返回一个附加的字符串,但我收到以下错误:

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

【问题讨论】:

标签: c return strcpy strcat


【解决方案1】:

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

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

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

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

还请注意,在完成分配的内存块后,free 它可以避免内存泄漏,这是一个很好的做法。释放内存后,您将无法再次访问该块。

【讨论】:

  • Sitenote:解决方案中的封装/生命周期/责任设计缺陷:CALLER 初始化了一个 Malloc - 但 CALLED 必须释放它。此外,如果您不清理/检查赋予函数的值,您可能很容易在堆中分配一个非常大的块......
  • 只是一个解释对象生命周期的例子。
【解决方案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
}

编译时,您会收到 [预期] 警告:

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

总结:

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

【讨论】:

  • 我对 c 完全是个菜鸟。根据以前的答案:使用引用调用将使用在调用者范围内声明的变量,这就是它被保留的原因,并且使用 malloc 需要在之后释放该内存以避免内存泄漏。这在这里如何应用?指针是在被调用函数的范围内创建的,我们如何知道该指针所寻址的内存区域在我们想要读取它时保持不变?谢谢
  • @Shyri hm,很深的问题,尤其是第二部分。我现在也想知道。您要问的基本上是堆如何确保其上的指针没有损坏..?
  • @Shyri 这段代码有点缺陷,因为你怀疑是 100% 正确的。一旦函数返回,不能保证数组会被保留。用 C++ 试试同样的方法。使用自定义析构函数创建一个类。您会注意到,一旦函数作用域结束,就会调用析构函数。但是您仍然可以访问该数组。 IMO 很危险。
【解决方案4】:

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

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

char *a = malloc(1000); 

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

【讨论】:

    【解决方案5】:

    这一行:

    char b = "blah";
    

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

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

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

    【讨论】:

      【解决方案6】:

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

      char *a = malloc(1000);
      

      然后在返回的指针上调用free

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

      【讨论】:

        【解决方案7】:
        char b = "blah"; 
        

        应该是:

        char *b = "blah"; 
        

        【讨论】:

        • 这不会有什么不同。阅读我的答案为什么!
        • @Gewure: "this won't make a difference" 它会,因为字符串文字 not 存在于函数的堆栈中,但 @ 987654321@
        • @alk 你是对的,这有点奇怪! :) 它相当骇人听闻和含蓄。杰里应该在他的回答中解释这一点!
        【解决方案8】:

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

        但是,我想添加其他信息。

        我现在遇到了同样的问题,我想要输出 函数是一个向量。

        在这种情况下,常见的解决方案是将输出声明为函数本身的参数。这样,变量的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;
              }
            //..
        }
        

        【讨论】:

          猜你喜欢
          • 2014-04-12
          • 2011-10-17
          • 2021-12-12
          • 2016-06-13
          • 2015-04-25
          • 2018-01-01
          相关资源
          最近更新 更多