我遇到了一些使用gcc编译器构建的C代码的问题。有问题的代码有一个枚举,其值在switch语句中用作配置对象的大小写。相当标准的东西。
当我使用-O0
选项标志编译代码时,所有内容构建并正确运行没有问题。但是,当标志设置为-O2
时,代码将不再按预期工作。
当我单步执行代码并对局部变量进行监视时,枚举(实际上应该只是三个枚举值之一)实际上是-104!这会导致程序无法配置对象。
有没有人能够提供一些指导?我以前没有遇到过这种情况,如果有人能解释为什么编译器会这样做,我会很感激。所以我可以进行任何必要的修改。
有问题的代码片段:
value = 0u;
switch(test_config) {
case DISABLE:
break;
case INTERNAL:
value = 1u;
break;
case EXTERNAL:
value = 2u;
break;
default:
valid = FALSE;
break;
}
if (valid) {
configure_test(value);
}
有问题的人:
typedef enum {
DISABLE,
INTERNAL,
EXTERNAL
} test_config_t;
这是导致问题的代码。我最初没有包含它,因为我不想要问题请修复我的代码,而是我一直在谷歌上查找为什么gcc优化标志会为同一段代码生成不同结果并且没有找到任何内容的原因特别有用。此外,我不在我的电脑,不得不在我的手机上打字,这也无济于事。所以我来到这里是因为这里的专家知道的方式比我更能指出正确的方向。
我可能应该包含的更多信息。代码在硬件上运行也可能是问题,我也在研究它。从FSBL运行时,代码使用-O0,但不使用-O2。所以它可能是硬件,但后来我不知道为什么它以另一种方式工作。
答案 0 :(得分:1)
你没有提供足够的详细信息(因为你的问题没有显示任何实际的代码,它应该有一些MCVE),但你很可能有一些undefined behavior,你应该是scared。
请注意,C11或C99(与大多数programming languages一样)是由显式规范定义的(不仅仅是代码中观察到的具体行为)用英语编写,部分定义有效C程序的运行时行为。阅读n1570。
我强烈建议您在阅读或编译源代码之前阅读Lattner的博客What Every C programmer should know about Undefined Behavior。
我建议至少使用(几乎)所有警告和调试信息进行编译,例如:使用gcc -Wall -Wextra -g
,然后改进代码以获取警告,并在gdb
调试器和valgrind下运行它。详细了解Invoking GCC。您也可以(暂时)使用一些清洁剂instrumentation options,尤其是-fsanitize=undefined
和-fsanitize=address
。您还可以将-std=gnu99
和-pedantic
添加到编译器标志中。请注意,gdb
watchpoints是一个非常有用的调试器功能,可以找到值更改或意外的原因。
当你为发布版编译或者在启用优化的基准测试时,还要保留警告标志(所以用gcc -O2 -Wall -Wextra
编译);优化可能会给出额外的警告,你也应该纠正它们。顺便说一句,GCC同时接受-O2
和-g
。
当你观察到这些问题时,在怀疑编译器之前首先询问你自己的代码(因为编译器经过了很好的测试;我在近40年的编程中发现只有一个编译器错误)。