通过指针修改const变量出现异常现象的原因探究
我为学习指针与const的相关知识,写了一段C语言代码:
int main() { const int value = 2; int *pvalue = &value; *pvalue = 3; printf("value: %d\n", value); printf("*pvalue: %d\n", *pvalue); printf("&value: %p\n", &value); printf("pvalue: %p", pvalue); return 0; }
我知道const变量value不能直接修改,但觉得可以通过*pvalue间接修改,这是我对编译器在int *pvalue = &value;行抛出的「warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]」警告的理解。
但运行代码后得到如下输出:
value: 2 *pvalue: 3 &value: 000000000060FE1C pvalue: 000000000060FE1C
明明指向同一内存地址,value和*pvalue的值却不一样,我忽略了什么?
原因解析
这是编译器常量折叠导致的结果,同时你这段代码触发了C语言中的未定义行为,具体拆解如下:
const的语义与编译器优化
const关键字在C语言里主要是给编译器的约束标记,用来表示变量「不应被修改」。对于const int value = 2;这种初始化时就赋值字面量的const变量,编译器会将其视为「编译期常量」——在编译阶段直接把所有用到value的地方替换成字面量2,而非运行时去内存地址读取值。指针强制转换的未定义行为
你把const int*强制转换成int*的操作,本身就违反了const的约定,编译器的警告已经明确提示了风险。之后通过*pvalue = 3;修改这块内存,属于C标准里的未定义行为——这种操作的结果没有任何保证,不同编译器、不同编译选项下可能出现各种异常结果。同地址不同值的本质
运行时你通过指针确实修改了内存地址000000000060FE1C里的值,但因为常量折叠优化,printf("value: %d\n", value);在编译时已经被替换成printf("value: %d\n", 2);,所以输出固定的2;而*pvalue是运行时去内存地址读取的,所以输出修改后的3。看似同地址不同值,本质是编译器优化造成的「假象」——代码里的value已经不是内存中的那个变量了。额外提醒
永远不要试图绕过const去修改变量,这种行为完全不符合C语言规范,除了出现奇怪的输出,还可能导致程序崩溃、数据损坏等更严重的问题。如果确实需要修改变量,一开始就不要将其声明为const。
内容的提问来源于stack exchange,提问作者Pascal Bergeron

