我有以下 C 代码:
#include <stdlib.h>
#include <stdio.h>
char* foo() {
char abc[4] = "abc";
return abc;
}
int main() {
printf("%s", foo());
return 0;
}
如果我用 gcc 编译它并运行可执行文件,我会得到 (null)%
作为输出。
如果我运行稍微修改过的代码:
#include <stdlib.h>
#include <stdio.h>
char* foo() {
char abc[4] = "abc";
return abc;
}
int main() {
printf("%c", *(foo()));
return 0;
}
我遇到了段错误。
我的问题是:为什么我的第一个代码不会出现段错误?我正在运行Linux和gcc版本:gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
这两个代码在编译时都会生成一个警告:函数返回局部变量的地址[-Wreturn-local-addr]
警告
最佳答案
当return abc;
开始执行时,abc
是一个指向foo
内部定义的数组的指针。 (正式地,它指定数组本身,但它会自动转换为第一个元素的地址。)该函数将返回此指针值。但是,当函数执行结束时,数组的生命周期也结束。
根据 C 2018 6.2.4 2:
The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
当 C 中的值不确定时,它的行为可能就像具有任何值一样,包括每次尝试使用它时都具有不同的值或具有陷阱值 (C 2018 3.19. 2和3.19.3)。请注意,这不仅意味着指针值指向的内容是不确定的;还意味着指针值指向的内容是不确定的。 指针本身的值是不确定的。
因此,即使 abc
在内存中有某个地址(例如 100400),也不意味着 100400 返回给调用者。返回给调用者的值是不确定的:它可以是任何值,包括空指针值。
编译器的优化器似乎已通过提供或允许空指针值作为函数 foo
的返回值来响应代码中未定义的行为。这是 C 标准所允许的。
当您将此空指针传递给 printf
以便与 %s
一起使用时,您的 printf
实现检查了该指针,发现它是一个空值指针,并打印“(null)”,而不是尝试使用它来访问内存中的字符串。
当您尝试使用*(foo())
取消引用指针时,没有对指针值进行初步检查。程序的机器代码尝试使用空指针访问内存,这导致了段错误。
关于c - C 中函数返回局部变量地址时的 NULL 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63861706/