C 编译器行为不匹配
Mismatch in C compiler behavior
我写了下面的代码来测试它是否可以通过指针 a 修改常量 c。我已经在 Clang、VC 和 GCC 编译器中对其进行了测试,并观察到 VC 和 GCC 代码都按我预期的方式工作,它打印 6 到标准输出,而当我用 Clang 编译它时value 没有被修改,并且 5 被打印到标准输出。
#include <stdio.h>
int main(void) {
const int c = 5;
int* a = (int*) &c;
*a = 6;
printf("%d", c);
return 0;
}
我想知道对此是否有任何众所周知的解释,或者它与编译器的内部结构和其他难以分析的东西有关。提前谢谢大家!
是的。它被称为未定义的行为。 C11 6.7.3p6:
- If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
具有未定义的行为explained as:
undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
NOTE: Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
(强调我的)
在未启用优化的情况下进行编译就像购买法拉利并始终仅以一档行驶一样。如果您使用 gcc
并使用 -O3
编译会怎样?
使用 -O3
我的 GCC 生成的代码等同于
#include <stdio.h>
int main(void) {
printf("%d", 5);
return 0;
}
那么这个程序怎么样:
#include <stdio.h>
int main(void) {
const char *foo = "Hello world!";
char *bar = (char*)foo;
bar[0] = 'C';
printf("%s\n", foo);
}
使用我的 GCC 和 -O3
它崩溃了。但是,如果您使用 Clang,它会打印 Hello world!
... 如果您查看程序集,Clang 会发现它可以优化为
int main(void) {
puts("Hello world!");
}
此代码的常见可能性包括:
- 编译器发现
const int c = 5;
意味着 c
的唯一定义值是 5,因此为 printf("%d", c);
生成的代码打印“5”而不加载 [=11] 的值=] 从记忆中或检查关于 c
. 的任何其他内容
- 编译器将
c
放入堆栈。然后int* a = (int*) &c;
取c
的地址。对于 *a = 6;
,编译器生成的代码将 6 写入 a
指向的位置,也就是存储 c
的位置。由于堆栈的使用方式,将其或其中的一部分设为只读是不切实际的,因此处理器不会为将 6 写入 C 中定义的 const
位置的代码生成任何异常. 然后 printf("%d", c);
从内存中取出 c
的值,得到 6,然后打印“6”。
None 当然,这是由 C 标准定义的,但这些行为是编译器、硬件和操作系统设计方式的结果。
您可能更有可能看到前者的行为具有高级别的优化,而后者的行为则关闭了优化,但它可能会有所不同。
我写了下面的代码来测试它是否可以通过指针 a 修改常量 c。我已经在 Clang、VC 和 GCC 编译器中对其进行了测试,并观察到 VC 和 GCC 代码都按我预期的方式工作,它打印 6 到标准输出,而当我用 Clang 编译它时value 没有被修改,并且 5 被打印到标准输出。
#include <stdio.h>
int main(void) {
const int c = 5;
int* a = (int*) &c;
*a = 6;
printf("%d", c);
return 0;
}
我想知道对此是否有任何众所周知的解释,或者它与编译器的内部结构和其他难以分析的东西有关。提前谢谢大家!
是的。它被称为未定义的行为。 C11 6.7.3p6:
- If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
具有未定义的行为explained as:
undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
NOTE: Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
(强调我的)
在未启用优化的情况下进行编译就像购买法拉利并始终仅以一档行驶一样。如果您使用 gcc
并使用 -O3
编译会怎样?
使用 -O3
我的 GCC 生成的代码等同于
#include <stdio.h>
int main(void) {
printf("%d", 5);
return 0;
}
那么这个程序怎么样:
#include <stdio.h>
int main(void) {
const char *foo = "Hello world!";
char *bar = (char*)foo;
bar[0] = 'C';
printf("%s\n", foo);
}
使用我的 GCC 和 -O3
它崩溃了。但是,如果您使用 Clang,它会打印 Hello world!
... 如果您查看程序集,Clang 会发现它可以优化为
int main(void) {
puts("Hello world!");
}
此代码的常见可能性包括:
- 编译器发现
const int c = 5;
意味着c
的唯一定义值是 5,因此为printf("%d", c);
生成的代码打印“5”而不加载 [=11] 的值=] 从记忆中或检查关于c
. 的任何其他内容
- 编译器将
c
放入堆栈。然后int* a = (int*) &c;
取c
的地址。对于*a = 6;
,编译器生成的代码将 6 写入a
指向的位置,也就是存储c
的位置。由于堆栈的使用方式,将其或其中的一部分设为只读是不切实际的,因此处理器不会为将 6 写入 C 中定义的const
位置的代码生成任何异常. 然后printf("%d", c);
从内存中取出c
的值,得到 6,然后打印“6”。
None 当然,这是由 C 标准定义的,但这些行为是编译器、硬件和操作系统设计方式的结果。
您可能更有可能看到前者的行为具有高级别的优化,而后者的行为则关闭了优化,但它可能会有所不同。