它是安全的代码,还是编译器可以通过
p
优化访问,使得*p
会导致42
?
#include <stdio.h>
int i = 42;
int *d = &i;
void test(int const *p)
{
*d = 43;
printf("%d, %p\n", *p, (void *) p);
printf("%d, %p\n", *d, (void *) d);
}
int main() {
test(d);
return 0;
}
我发现
*p
通常是用43
打印的,但我想知道这里是否有任何陷阱,所以在某些情况下打印*p
可能会产生42
。
它是安全的代码吗?还是编译器可以通过 p 优化访问,使得 *p 的结果为 42?
安全。
如果这里有任何陷阱,那么在某些情况下打印 *p 可能会产生 42。
没有。
编译器如果应用此类优化,则必须意识到您正在修改可能通过另一个指针别名的全局变量,并且必须在每次访问时重新计算结果。
const
指针仅意味着您不能使用that指针修改内存。您可以考虑研究 C 编程语言中的 restrict
关键字和别名。
这里的一个关键误解可能是如何将参数传递给 C 中的函数。它们是按值传递的。
当
d
传递给 test
时,指针的值(内存地址)被复制到指向 const 的指针 p
中。两者都包含相同的内存地址,但只有 p
的目标受到 const
的约束。
如果您尝试通过修改
*p
来更新该地址处的数据,则会遇到错误,但 d
没有类似的约束,因此 *d
可以用于更新该内存地址处的数据。
两个指针仍然指向相同的数据,因此打印
p
指向的 int 显示 43
。
@Chris 已经解释了为什么
*p
必然会产生 43
。但这里还有更多要补充的:
这个密码安全吗?
您的特定程序是安全的,但它演示的那种编程通常不是很安全。
这些问题之一是通过指针使用别名有点容易出错:您更有可能忘记考虑对同一数据的多种访问。这并不是说您应该完全避免它,而是应该努力将其最小化。
另一个是使用全局变量:这并不是一个坏主意,但是 - 程序的可测试性、可读性、可理解性、可预测性通常可以通过避免它们来提高。请参阅此 StackOverflow 问题中的讨论:
现在,在您的具体情况下:如果您已经将指针传递给
i
- 为什么要使 d
全局化?为什么首先要让 i
全球化?
例如,您可以写:
void test(int const *p)
{
// can only use p or *p here - not d nor i
}
int main() {
int i = 42;
int *d = &i;
test(d);
return 0;
}
没有全局变量,并且
i
的生命周期为 d
是程序的整个持续时间。您只需更严格地控制谁有权访问什么。
还有一些挑剔:
d
和 p
作为变量名时,他们可能会凭直觉认为 p
是一个指针,但他们很可能认为 d
不是。当然他们可以去阅读变量声明,但有时距离很远,读者不会这样做。