我知道,这是一个非常非常常见的问题。我也读过这个、那个和这个。我曾经认为,返回局部变量的地址是一个非常糟糕的主意。我曾经认为你应该做得更好:
- 通过例如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;
}
我用迂腐的错误编译并获得预期的警告:函数返回本地变量[-Wreturn local-addr]的地址
但仅适用于foo2()!foo1()运行良好。有人知道为什么会有这种不明确的行为吗?
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;
}
返回CCD_ 10的值。
在foo_1()
中,由于返回值的变量恰好是指针,因此如果该值有问题,则仍然可以调用未定义的行为。例如:
int foo_1a(void) {
char arr[] = "bar";
char *p = arr; // or equivalently, &arr[0]
return p;
}
在这里,您仍然返回局部变量的值(这很好),但该值恰好是另一个局部变量的地址,因此函数一返回,返回的指针值就变为无效。
编译器警告foo_1a
的可能性比警告foo_2
的可能性小,因为在执行返回语句时,它不太可能确定p
的值有问题。事实上,这种语言不需要对这种事情进行诊断。编译器可以很好地检测和警告某些但不是所有未定义行为的实例。
一句话:您的foo_1()
函数表现良好。它返回的指针值是字符串文字的地址,它具有静态存储持续时间(即,它在程序的整个生命周期中都存在)。
但是,由于修改该静态数组具有未定义的行为,因此明智的做法是将地址返回为const char*
,而不是char*
,因此调用者不太可能尝试修改字符串文字。const
还可以作为任何人类读者的文档,说明指向的值是不可修改的。
char * p_1 = foo_1();
只要不修改p_1
指向的任何内容,取消引用p_1
就不是问题,因为foo_1
返回一个指向存储在程序只读部分中的字符串文字的指针。
char ** p_2 = foo_2();
不正常。由于p_2
指向已删除的对象,因此取消引用p_2
是导致未定义行为的原因。