又一个" return-local-addr" - "函数返回局部变量的地址"

时间:2015-03-04 21:06:00

标签: c

我知道,这是一个非常非常常见的问题。 我读过例如thisthatand this too。 我曾经认为返回局部变量的地址是一个非常糟糕的主意。我曾经认为你应该更好:

  • 通过例如分配内存的malloc
  • 将局部变量设为静态或
  • 将指针作为参数传递

然后我尝试了这个:

#include <stdio.h>
char *  foo_1();
char ** foo_2();

int main() {

    char * p_1 = foo_1();
    char ** p_2 = foo_2();

    printf("\n [%s] \n", p_1);
    printf("\n [%s] \n", *p_2);

return 0;
}

char * foo_1() {
    char * p = "bar";
    return p;
}

char ** foo_2() {
    char * p = "bar";
    return &p;
}

我使用-pedantic -pedantic-errors进行编译 并得到预期的警告:函数返回局部变量的地址[-Wreturn-local-addr]

但仅适用于foo_2()! foo_1()工作正常。有没有人知道为什么以及这种未定义的行为?

4 个答案:

答案 0 :(得分:4)

char * foo_1() {
    char * p = "bar";
    return p;
}

这里不是返回本地对象的地址,而是指向指向字符串文字的指针的指针值。字符串文字具有静态存储持续时间,可以返回指向字符串文字的指针。当foo_1返回时,p对象被销毁(自动存储持续时间),但不会被"bar"(静态存储持续时间)。

答案 1 :(得分:3)

foo_2()中,您将返回本地变量p的地址,这就是您收到警告的原因。

foo_1()中,您将返回文字字符串的地址,因为它具有静态存储持续时间,所以很好。

答案 2 :(得分:2)

foo_2()中,您将返回(非static)局部变量的地址。当函数返回时,该地址变得不确定,因为指向的对象不再存在 - 因此警告。

foo_1()中,您将返回局部变量的。一切都没有问题;它并不比以下更糟糕:

int foo_3(void) {
    int local = 42;
    return local;
}

返回local

foo_1()中,由于您返回的值恰好是指针的变量,如果该值有问题, 仍然可以调用未定义的行为。例如:

int foo_1a(void) {
    char arr[] = "bar";
    char *p = arr; // or equivalently, &arr[0]
    return p;
}

这里你仍然返回局部变量的值(这很好),但是该值恰好是另一个局部变量的地址,因此一旦函数返回,返回的指针值就变为无效。

编译器不太可能警告foo_1a而不是关于foo_2,因为在执行return语句时,它不太可能确定p的值是有问题的。事实上,该语言不需要对此类事物进行诊断。编译器可以很好地检测和警告某些但不是所有未定义行为的实例。

底线:您的foo_1()功能表现良好。它返回的指针值是字符串文字的地址,它具有静态存储持续时间(即,它存在于程序的整个生命周期中)。

但是,由于修改静态数组具有未定义的行为,因此将地址作为const char*而不是char*返回是明智的,因此调用者不太可能尝试修改字符串字面量。 const也可作为任何人类读者的文档,指出不得修改指向的值。

答案 3 :(得分:0)

char * p_1 = foo_1();

没关系。取消引用p_1不是问题,只要您不修改p_1指向的任何内容,因为foo_1返回指向存储在只读部分中的字符串文字的指针该计划。

char ** p_2 = foo_2();

不行。取消引用p_2是未定义行为的原因,因为p_2指向已被删除的对象。