当另一个指向 const 的指针观察到数据时,通过指针更改数据是否安全?

问题描述 投票:0回答:3

它是安全的代码,还是编译器可以通过

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

c constants compiler-optimization pointer-aliasing
3个回答
3
投票

它是安全的代码吗?还是编译器可以通过 p 优化访问,使得 *p 的结果为 42?

安全。

如果这里有任何陷阱,那么在某些情况下打印 *p 可能会产生 42。

没有。

编译器如果应用此类优化,则必须意识到您正在修改可能通过另一个指针别名的全局变量,并且必须在每次访问时重新计算结果。

const
指针仅意味着您不能使用that指针修改内存。您可以考虑研究 C 编程语言中的
restrict
关键字和别名。


2
投票

这里的一个关键误解可能是如何将参数传递给 C 中的函数。它们是按值传递的。

d
传递给
test
时,指针的值(内存地址)被复制到指向 const 的指针
p
中。两者都包含相同的内存地址,但只有
p
的目标受到
const
的约束。

如果您尝试通过修改

*p
来更新该地址处的数据,则会遇到错误,但
d
没有类似的约束,因此
*d
可以用于更新该内存地址处的数据。

两个指针仍然指向相同的数据,因此打印

p
指向的 int 显示
43


1
投票

@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
    不是。当然他们可以去阅读变量声明,但有时距离很远,读者不会这样做。
© www.soinside.com 2019 - 2024. All rights reserved.