在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
语义做一个最终的运行。 但是,你可能不会声明x
是const
。
不好的程序员。 没有月饼!
如果你需要修改一个const,把它复制到一个非constvariables,然后使用它。 这是常数的原因。 试图“潜入”const可能会导致严重的运行时问题。 即优化器可能使用内联值等。
const int x=1; int non_const_x = x; non_const_x = 2;
这里有一个很好的讨论: 邪恶的编译器是否被邪恶的怪物所欺骗?
我希望gcc编译这个,因为:
- ptr被允许指向x,否则读取它将是不可能的,虽然正如下面的注释所说,它不是完美的代码,编译器应该抱怨。 警告选项将(我猜)会影响它是否实际上被警告。
- 当你写入x时,编译器不再“知道”它正在写入一个const,所有这些都在编码器手中。但是,它确实知道,所以它可能会警告你,这取决于警告选项你select了。
不pipe它是否工作,取决于编译代码的方式,所选编译选项以及目标CPU和体系结构的常量。 它可能工作。 或者它可能会崩溃。 或者你可以写一个“随机”的内存位,并导致(或不)一些怪异的效果。 这不是一个好的编码策略,但这不是你的问题:-)