作者:赵春柱_626 | 来源:互联网 | 2023-02-01 12:57
我最近在用C++编程了一段时间之后再次开始用C编程,而我对指针的理解有点生疏.
我想问一下为什么这段代码没有导致任何错误:
char* a = NULL;
{
char* b = "stackoverflow";
a = b;
}
puts(a);
我认为因为b
超出范围,a
应该引用一个不存在的内存位置,因此它们在调用时会出现运行时错误printf
.
我在MSVC中运行此代码大约20次,并且没有显示错误.
1> dbush..:
在b
定义的范围内,为其分配字符串文字的地址.这些文字通常位于内存的只读部分,而不是堆栈.
当你做a=b
你指定值的b
对a
,即a
现在包含一个字符串的地址.b
超出范围后,此地址仍然有效.
如果你采取了地址的b
,然后试图取消引用该地址,那么你就调用未定义行为.
所以,你的代码是有效的,并没有发生未定义行为,但下列情况:
int *a = NULL;
{
int b = 6;
a = &b;
}
printf("b=%d\n", *a);
另一个更微妙的例子:
char *a = NULL;
{
char b[] = "stackoverflow";
a = b;
}
printf(a);
此示例与您的示例之间的区别在于b
,它是一个数组,在指定时会衰减到指向第一个元素的指针a
.所以在这种情况下a
包含一个局部变量的地址,然后超出范围.
编辑:
作为旁注,将变量作为第一个参数传递是不好的做法printf
,因为这会导致格式字符串漏洞.最好使用字符串常量如下:
printf("%s", a);
或者更简单:
puts(a);
我认为即使后一个代码也可能优化为`printf("stackoverflow")`取决于你正在使用的编译器/开关
@CometEngine很高兴我能提供帮助.如果您发现它有用,请随意[接受此答案](/sf/ask/17360801/).
@Govind Parmar刚刚在msvc上进行了全面优化和内联测试; 似乎是这样的.
2> SiggiSv..:
逐行,这是您的代码所做的:
char* a = NULL;
a
是一个不引用任何东西的指针(设置为NULL
).
{
char* b = "stackoverflow";
b
是一个引用静态常量字符串文字的指针"stackoverflow"
.
a = b;
a
设置为也引用静态,常量字符串文字"stackoverflow"
.
}
b
超出范围.但是,由于a
是没有引用b
,那么这也不要紧(它只是引用相同的静态常量字符串文字作为b
被引用).
printf(a);
打印"stackoverflow"
引用的静态常量字符串文字a
.
空指针不"指向NULL".它*是*NULL.
3> kyle..:
字符串文字是静态分配的,因此指针无限期有效.如果您已经说过char b[] = "stackoverflow"
,那么您将在堆栈上分配一个char数组,当该范围结束时该数组将变为无效.这种差异也表现为修改字符串:char s[] = "foo"
stack分配一个你可以修改的字符串,而char *s = "foo"
只给你一个指向可以放在只读内存中的字符串的指针,所以修改它是未定义的行为.
4> zwol..:
其他人已经解释说这段代码完全有效.这个答案是关于你的期望,如果代码无效,调用时会出现运行时错误printf
.不一定如此.
让我们看一下代码中的这种变化,这是无效的:
#include
int main(void)
{
int *a;
{
int b = 42;
a = &b;
}
printf("%d\n", *a); // undefined behavior
return 0;
}
这个程序有不确定的行为,但它恰好是相当可能的,这将在事实上,印刷品42几个不同的原因-许多编译器将离开堆栈槽b
分配的整个身体main
,因为没有别的需要的空间和最小化堆栈调整的数量简化了代码生成; 即使编译没有正式解除分配栈槽,数字42很可能保留在内存中,直到别的东西覆盖它,并没有什么之间a = &b
,并*a
做到这一点; 标准优化("常量和复制传播")可以消除这两个变量,并将最后已知的值*a
直接写入printf
语句中(就像您已经编写过一样printf("%d\n", 42)
).
理解"未定义的行为"并不意味着"程序会以可预测的方式崩溃",这一点至关重要.这意味着"任何事情都可能发生",而且任何事情都包括看起来像程序员可能想要的那样(在这台计算机上,今天用这个编译器).
作为最后一点,我没有方便访问(Valgrind,ASan,UBSan)的激进调试工具,没有足够详细的跟踪"自动"变量生命周期来捕获此错误,但是GCC 6确实产生了这个有趣的警告:
$ gcc -std=c11 -O2 -W -Wall -pedantic test.c
test.c: In function ‘main’:
test.c:9:5: warning: ‘b’ is used uninitialized in this function
printf("%d\n", *a); // undefined behavior
^~~~~~~~~~~~~~~~~~
我相信这里发生的事情是,它没有我上述优化-复制的最后已知值b
进*a
再进printf
-但它的"最后已知值"的b
是"这个变量是未初始化的"定点,而不是42(这然后生成相当于printf("%d\n", 0)
.)的代码.)