在C中,可以通过一个指针修改一个constvariables?

我在代码中写了一些类似的东西

const int x=1; int *ptr; ptr = &x; *ptr = 2; 

这是否适用于所有编译器? 为什么GCC编译器没有注意到我们正在改变一个常量variables?

const实际上并不意味着“常量”。 C中的“常量”是一个在编译时确定的值; 字面42是一个例子。 const关键字的意思是只读的。 考虑一下,例如:

 const int r = rand(); 

在程序执行时间之前, r的值是不确定的,但const关键字意味着在初始化之后不允许修改r

在你的代码中:

 const int x=1; int *ptr; ptr = &x; *ptr = 2; 

赋值ptr = &x;违反约束条件的 ,意味着需要一个符合要求的编译器来抱怨; 您不能合法地将一个const int* (指向const int)值赋给一个非const int*对象。 如果编译器生成一个可执行文件(不需要这样做,它可能只是拒绝它),那么这个行为就不是由C标准定义的。

例如,生成的代码实际上可能将值2存储在x – 但是稍后对x引用可能会产生值1 ,因为编译器知道 x在其初始化之后不能被修改。 而且它知道,因为你这样说,通过将x定义为const 。 如果你对编译器说谎,后果可能是任意的。

实际上,最糟糕的情况是程序的行为和你期望的一样。 这意味着你有一个很难察觉的错误。 (但你应该得到的诊断将是一个很大的线索。)

Online C 2011草案 :

6.7.3types合格者


6 如果尝试通过使用非常量限定types的左值来修改定义了常量限定types的对象,则行为是不确定的 。 如果试图通过使用具有非易失性限定types的左值来引用定义为具有挥发性限定types的对象,则行为是不确定的。 133)


133)这适用于那些performance得好像被定义为合格types的对象,即使它们从来没有被实际定义为程序中的对象(例如存储器映射的input/输出地址处的对象)。

重点补充。

由于行为没有定义,编译器不需要发出诊断,也不需要停止转换。 这在一般情况下是难以理解的; 假设你有一个像这样的函数

 void foo( int *p ) { *p = ...; } 

定义在它自己的独立翻译单位。 在翻译期间,编译器无法知道p是否可以指向一个const限定的对象。 如果你的电话是类似的

 const int x; foo( &x ); 

可能会得到类似于parameter 1 of 'foo' discards qualifiers警告parameter 1 of 'foo' discards qualifiers或类似的照明。

还要注意, const限定符并不一定意味着相关variables将被存储在只读存储器中,所以上面的代码可能会“工作”(更新x的值),因为您已经成功更新了x围绕const语义做一个最终的运行。 但是,你可能不会声明xconst

不好的程序员。 没有月饼!

如果你需要修改一个const,把它复制到一个非constvariables,然后使用它。 这是常数的原因。 试图“潜入”const可能会导致严重的运行时问题。 即优化器可能使用内联值等。

 const int x=1; int non_const_x = x; non_const_x = 2; 

这里有一个很好的讨论: 邪恶的编译器是否被邪恶的怪物所欺骗?

我希望gcc编译这个,因为:

  • ptr被允许指向x,否则读取它将是不可能的,虽然正如下面的注释所说,它不是完美的代码,编译器应该抱怨。 警告选项将(我猜)会影响它是否实际上被警告。
  • 当你写入x时,编译器不再“知道”它正在写入一个const,所有这些都在编码器手中。但是,它确实知道,所以它可能会警告你,这取决于警告选项你select了。

不pipe它是否工作,取决于编译代码的方式,所选编译选项以及目标CPU和体系结构的常量。 它可能工作。 或者它可能会崩溃。 或者你可以写一个“随机”的内存位,并导致(或不)一些怪异的效果。 这不是一个好的编码策略,但这不是你的问题:-)