又一个“return-local-addr” – “函数返回局部变量的地址”
我知道,这是一个非常非常常见的问题。 我已经读过这样的 , 那个 , 也是这个 。 我曾经认为返回局部变量的地址是一个非常糟糕的主意。 我曾经认为你应该更好:
- 通过例如malloc分配内存
- 使局部变量静态或
- 将指针作为参数传递
但后来我尝试了这个:
#include 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()工作正常。 有没有人知道为什么以及这种未定义的行为?
char * foo_1() { char * p = "bar"; return p; }
在这里,您不返回本地对象的地址,而是返回指向字符串文字的指针的指针值。 字符串文字具有静态存储持续时间,可以返回指向字符串文字的指针。 当foo_1
返回时, p
对象被销毁(自动存储持续时间)但不是"bar"
(静态存储持续时间)。
在foo_2()
您将返回局部变量p
的地址,这就是您收到警告的原因。
在foo_1()
您将返回文字字符串的地址,这很好,因为它具有静态存储持续时间。
在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*
而不是const char*
返回是明智的,因此调用者不太可能尝试修改字符串文字。 const
还可以作为任何人类读者的文档,指出不需要修改指向的值。
char * p_1 = foo_1();
没关系 只要您不修改p_1
指向的任何内容, foo_1
引用p_1
就不是问题,因为foo_1
返回指向存储在程序的只读部分中的字符串文字的指针。
char ** p_2 = foo_2();
不行。 取消引用p_2
是未定义行为的原因,因为p_2
指向已删除的对象。