另一个"return-local-addr"——"function returns address of local variable"



我知道,这是一个非常非常常见的问题。我也读过这个、那个和这个。我曾经认为,返回局部变量的地址是一个非常糟糕的主意。我曾经认为你应该做得更好:

  • 通过例如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是导致未定义行为的原因。

相关内容