C语言--通过别名非const指针修改const

4

在标准C中,一个函数是否可以使用别名指向的int *修改const int *类型的参数呢?换句话说,下面的代码是否保证在标准C中始终返回42和1?

#include <stdio.h>

void foo(const int *a, int *b)
{
    printf("%d\n", *a);
    *b = 1;
    printf("%d\n", *a);
}

int main(void)
{
    int a = 42;
    foo(&a, &a);
    return 0;
}
4个回答

8
在你的示例代码中,有一个整数。你使用一个常量指针和一个非常量指针来引用它。通过非常量指针修改整数是合法和明确定义的。
由于两个指针都指向整数,且常量指针不必指向常量对象,编译器应该预期从常量指针读取的值可能已发生更改,并需要重新加载该值。
请注意,如果你使用了“restrict”关键字,则情况将不同,因为它指定指针参数不与任何其他指针参数别名,因此编译器可以优化重新加载操作。

因为有些人根据其他答案的状态而不是他们自己的知识来投票。这种“逻辑”是这样的:既然有另一个被踩的回答说是,那么我的回答也一定是错误的。另一个解释是,有人只是错了,但是非常热心地投票。 - this
提问的那个人自从提问以来就掉了3分,所以我猜他们可能被踩了,如果每次点击会扣1分的话。 - Darryl Miles
@DarrylMiles 什么?那跟什么有关系? - Iskar Jarak
关于负分投票政策,我也在想谁会在不留评论的情况下对新回答进行负分投票?也许有人可以指引我查看关于这个问题的 Stack Overflow 传说?这与此处所做的评论有关。 - Darryl Miles
@DarrylMiles 人们总是以奇怪的方式投票(赞成或反对)。我认为没有可执行的政策,但如果你想听谣言,可以尝试搜索元数据。最近我读到了一件有趣的事情,大意是:“看看你认为非常愚蠢的所有问题。现在意识到,像那样的问题表明了一些用户的重要部分,他们可能会很乐意支持这些问题。” 同样的道理也适用于反对票。 - Iskar Jarak
@DarrylMiles 关于原帖(可能)失去3个声望值,投反对票会损失1个声望值,但反对票会使得问题失去2个声望值,且该问题已经有1个反对票,所以我们不应该从中做任何假设,这就是我对你最初评论感到困惑的原因。 - Iskar Jarak

1
是的,你的程序已经定义好了。 事实上,通过指向非const int变量的指针指向const int,不会使该变量变为const,它仍然可以通过指向int的指针或使用原始变量标签进行修改。

0

可以这样做(如果您知道可以逃脱制裁)。

原因之一,您可能无法逃脱制裁,是因为您要写入的目标内存位于只读受保护区域中(例如常量数据),则会引发访问冲突。例如,在编译时任何常量都会以可执行文件的只读数据段形式存在。大多数平台支持在运行时防止写入它。

基本上不要这样做。

您的示例还存在其他问题,这可能不是最好的演示方式。例如,在第二个printf中需要重新加载*a,编译器可能将其优化掉!(它知道'a'没有改变,它知道'a'指向一个const,因此,它不需要通过执行内存加载操作来重新加载内存以获取第二个'*a'表达式的值,它可以重用它可能已经在寄存器中的值,因为它在第一次加载'*a'时就已经有了)。现在,如果您在之间添加一个内存屏障,那么您的示例可能会更好地工作。

https://en.wikipedia.org/wiki/Memory_barrier GCC? asm volatile("":::"memory"); //在第二个printf之前可能有效

但是针对你所问的实际问题,如果你了解其他相关内容,是可以做到的。


任何优化了reload out的编译器,对于第二个printf来说都是垃圾。不需要屏障。然后你的例子有更好的运行机会。我能说的只有:??? - this
请引用禁止此种优化的规范。 - Darryl Miles
1
你在争辩它可以优化掉,因此你必须引用允许这样做的部分。(当然这是不可能的。) - this
1
@DarrylMiles restrict 关键字的存在是为了允许这种优化 - 这不是默认允许发生的事情。 - Iskar Jarak
用GCC -O6编译成功了,函数声明为void foo(const int * const __restrict__ a, int * __restrict__ b),第二个'const'起了作用。现在在-O0和-O6下输出有所变化。我可以确认你是正确的,restrict允许进行这种优化。添加内存屏障也可以使其始终正常工作,强制进行第二次加载。 :) - Darryl Miles
显示剩余2条评论

-1

是的,它保证始终打印42和1。

const int *a 表示指向的值对于指针 a 是常量。

在函数中尝试从 a*a = 10;)解引用,您将收到一个错误。

然而,指针 a 不是常量。例如,您可以执行 a = b

b 可以指向与 a 相同的地址和/或修改该值,就像您在示例中所做的那样。 如果您声明 b 指针的值为常量(const int *b),则会收到一个错误。

我试着这样记忆:

const int *a - a 指向一个 int 类型的对象,不允许修改它(任何其他指向该对象的指针都可以根据其声明/定义进行操作)。


你给出了正确的答案,但是你的解释使用了不正确的术语。(我没有点踩) - this
我明白了,没问题。原帖中根本没有使用“常量”,所以我想不会有什么混淆。 - Ely
3
@Elyasin 在我们谈论需要修复的问题时,"const int *a - a points to a read-only object of type int" 这句话并不是完全正确的。它不一定要指向一个只读对象,尽管它可能会指向这样的对象。它只是表示不能通过 const 限定的指针修改对象。 - Iskar Jarak
没错。但我认为这有点过于学术化了。在这个例子中没有强制转换的情况。当然让原帖作者知道也是好的。 - Ely
1
@Elyasin 无论你是否是学术界的人士,你修改后的版本都更好。 - Iskar Jarak
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接