非const int指针在const int上的行为

时间:2012-01-02 06:30:42

标签: pointers

#include<stdio.h>

int main()
{
       const int sum=100;
        int *p=(int *)&sum;

        *p=101; 


        printf("%d, %d",*p,sum);
        return 0;
}

/ *

输出

101,101

* /

p指向一个常数整数变量,那么为什么/ p如何设法改变和的值?

4 个答案:

答案 0 :(得分:6)

这是未定义的行为 - 这是代码中的错误。代码“似乎有效”这一事实毫无意义。允许编译器使程序崩溃,或者允许程序执行无意义的操作(例如更改应该是const的某些内容的值)。或者完全做其他事情。对行为进行“推理”毫无意义,因为对行为没有要求。

请注意,如果代码编译为C ++,您将收到错误,因为C ++不会隐式抛弃const。希望,即使编译为C,您也会收到警告。

答案 1 :(得分:2)

p包含变量sum的内存地址。语法*p表示sum的实际值。

当你说

*p=101

你说的是:转到地址p(这是存储变量sum的地址)并在那里更改值。所以你实际上正在改变sum

答案 2 :(得分:2)

你可以看到const作为编译时标志告诉编译器“我不应该修改这个变量,告诉我是否这样做。”它不会对是否实际修改变量强制执行任何操作。

由于 通过非const指针修改该变量,编译器确实会告诉你:

main.c: In function 'main':
main.c:6:16: warning: initialization discards qualifiers from pointer target type

你打破了自己的承诺,编译器警告你,但会让你愉快地继续前进。

答案 3 :(得分:1)

行为未定义,这意味着它可能会在不同的编译器实现,体系结构,编译器/优化器/链接器选项上产生不同的结果。


为了分析,这里是:

(免责声明:我不知道编译器。这只是对编译器如何选择处理这种情况的逻辑猜测,从一个简单的汇编语言调试器的角度来看。)

  • 当声明一个常量整数时,编译器可以选择使其可寻址或不可寻址。
    • 可寻址意味着整数值实际占用内存位置,这样:
      • 生命将是静止的。
      • 该值可能会硬编码到二进制文件中,或在程序启动期间初始化。
      • 可以使用指针访问它。
      • 可以从任何知道其地址的二进制代码访问它。
      • 它可以放在只读或可写内存部分。
      • 对于日常CPU,不可写性由内存管理单元(MMU)强制执行。从用户空间弄乱MMU是不可能的,仅仅是一个const整数值是不值得的。
      • 因此,为了简单起见,它将放入可写存储器部分。
      • 如果编译器选择将其放在不可写的内存中,则程序在尝试写入不可写内存时会崩溃(访问冲突)。
      • 将微控制器放在一边 - 如果您正在使用微控制器,就不会问这个问题。
    • 不可寻址意味着它不占用内存地址。相反,每个引用变量的代码(即使用该整数的值)都会收到一个r值,就好像你执行了一个find-and-replace来将sum的每个实例都改成一个文字{{1 }}。
      • 在某些情况下,编译器不能使整数不可寻址:如果编译器知道你正在获取它的地址,那么编译器肯定知道它必须放置该值在记忆中。您的代码属于这种情况。
      • 然而,通过一些积极优化的编译器,完全有可能使其不可寻址:变量可能已被消除,而100将变为{{1其中printfint main() { printf("%s, %s", (b1? "100" : "101"), (b2? "100" : "101")); return 0; }取决于编译器的情绪。
    • 编译器有时会做出一个分裂的决定 - 它可能会做其中一个,甚至是完全不同的东西:
      • 分配内存位置,但用常量字面值替换每个引用。发生这种情况时,调试器会告诉您值为零,但使用该位置的任何代码都会显示包含硬编码值。
    • 某些编译器可能能够检测到强制转换导致未定义的行为并拒绝编译。