又一个“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指向已删除的对象。