#include <stdio.h>
int main()
{
const int a = 12;
int *p;
p = &a;
*p = 70;
}
它会起作用吗?
#include <stdio.h>
int main()
{
const int a = 12;
int *p;
p = &a;
*p = 70;
}
它会起作用吗?
这是“未定义的行为”,这意味着根据标准,您无法预测尝试此操作时会发生什么。根据特定的机器、编译器和程序的状态,它可能会做不同的事情。
在这种情况下,最常发生的是答案是“是”。变量,无论是否为 const,只是内存中的一个位置,您可以打破 const 的规则并简单地覆盖它。(当然,如果程序的其他部分依赖于它的 const 数据是恒定的,这将导致严重的错误!)
然而,在某些情况下——最常见的是const static
数据——编译器可能会将这些变量放在内存的只读区域中。例如,MSVC 通常会将 const static ints 放在可执行文件的 .text 段中,这意味着如果您尝试对其进行写入,操作系统将抛出保护错误,并且程序将崩溃。
在编译器和机器的其他组合中,可能会发生完全不同的事情。您可以确定的一件事是,这种模式会惹恼任何必须阅读您的代码的人。
这是未定义的行为。证明:
/* program.c */
int main()
{
const int a = 12;
int* p;
p = &a;
*p = 70;
printf("%d\n", a);
return 0;
}
gcc program.c
并运行它。输出为 70 (gcc 4.3)
然后像这样编译它:
gcc -O2 program.c
并运行它。输出将是 12。当它进行优化时,编译器可能会将 12 加载到寄存器中,并且当它需要访问 printf 的 a 时不会费心再次加载它,因为它“知道” a 不能改变。
通过指针修改const
限定对象会调用未定义的行为,结果就是这样。它可能是您对特定实现的期望,例如先前的值未更改,如果它已放置在.text
等。
它确实适用于 gcc。但它不喜欢它:
test.c:6: 警告:赋值从指针目标类型中丢弃限定符
但是执行时值确实发生了变化。我不会指出明显的禁忌...
是的,你可以通过使用这样的代码来完成它。但是当什么时候是全局的时候代码不适用a
(一个gcc编译的程序给了我segmentation fault
。)
一般来说,在心爱的 C 中,您几乎总能找到某种方式来破解不应该更改或公开的内容。const 这里是一个例子。
但是考虑到可怜的人(可能是我 6 个月后)维护我们的代码,我经常选择不这样做。
这里指针的类型p
是int*
,它被分配了类型的值const int*
(&a
=>const int
变量的地址)。
隐式转换消除了常量,尽管 gcc 会引发警告(请注意,这在很大程度上取决于实现)。
由于指针未声明为const
,因此可以使用此类指针更改值。
如果将指针声明为const int* p = &a
,您将无法执行*p = 70
.
此代码包含约束违规:
const int a = 12;
int *p;
p = &a;
违反的约束是 C11 6.5.16.1/1 “简单赋值”;如果两个操作数都是指针,那么左边指向的类型必须具有右边指向的类型的所有限定符。(并且类型,无限定符,必须兼容)。
所以违反了约束,因为&a
has type const int *
,它const
作为限定符;但是那个限定符没有出现在 is 的类型p
中int *
。
编译器必须发出诊断并且可能不会生成可执行文件。任何可执行文件的行为都是完全未定义的,因为程序不符合语言规则。
坏,坏主意。
此外,行为是特定于平台和实现的。如果您在常量存储在不可写内存中的平台上运行,这显然行不通。
而且,你到底为什么要这样做?要么更新源中的常量,要么将其设为变量。
您不能通过使用指向它的指针来更改常量变量的值。这种类型的指针称为Pointer to a constant
.
还有一个概念叫做Constant Pointer
. 这意味着一旦指针指向一个内存位置,您就不能让它指向另一个位置。
更改变量值的问题const
是编译器不会期望这种情况发生。考虑这段代码:
const int a = 12;
int * p = &a;
*p = 70;
printf("%d\n", a);
为什么编译器会a
在最后一条语句中读取?编译器知道那a
是12
,因为它是const
,它永远不会改变。所以优化器可能会将上面的代码转换成这样:
const int a = 12;
int * p = &a;
*p = 70;
printf("%d\n", 12);
这可能会导致奇怪的问题。例如,代码可能在没有优化的调试版本中按需要工作,但在有优化的发布版本中会失败。
实际上,一个好的优化器可能会将整个代码转换为:
printf("%d\n", 12);
因为之前的所有其他代码在编译器眼中都没有影响。省略没有影响的代码也不会影响整个程序。
另一方面,一个体面的编译器会识别出你的代码有问题并警告你,因为
int * p = &a;
实际上是错误的。正确的是:
const int * p = &a;
asp
不是指向的指针int
,而是指向的指针const int
,当这样声明时,下一行将导致硬编译错误。
要摆脱警告,您必须强制转换:
int * p = (int *)&a;
一个更好的编译器会认识到这种转换违反了const
承诺并指示优化器不要将a
其视为const
.
如您所见,编译器的质量、功能和设置将最终决定您可以期待什么行为。这意味着相同的代码在不同的平台上或在同一平台上使用不同的编译器时可能会表现出不同的行为。
如果 C 标准为这种情况定义了一种行为,那么所有编译器都必须实现它,无论标准定义了什么,它都很难实现,给每个想要编写编译器的人带来巨大的负担。即使标准刚刚说“这是禁止的”,所有编译器都必须执行复杂的数据流分析来强制执行此规则。所以标准只是没有定义它。它定义了const
值不能更改,如果您无论如何都找到了更改它们的方法,则没有可以依赖的行为。
是的,您可以更改常量变量的值。
试试这个代码:
#include <stdio.h>
int main()
{
const int x=10;
int *p;
p=(int*)&x;
*p=12;
printf("%d",x);
}