对于赏金:如何在不停用或降低优化级别的情况下,根据具体情况禁用此行为?
以下条件表达式是在MinGW GCC 3.4.5上编译的,其中a
的类型为signed long
,而m
的类型为unsigned long
。
if (!a && m > 0x002 && m < 0x111)
使用的CFLAGS
为-g -O2
。这是相应的汇编GCC输出(转储objdump
)
120: 8b 5d d0 mov ebx,DWORD PTR [ebp-0x30]
123: 85 db test ebx,ebx
125: 0f 94 c0 sete al
128: 31 d2 xor edx,edx
12a: 83 7d d4 02 cmp DWORD PTR [ebp-0x2c],0x2
12e: 0f 97 c2 seta dl
131: 85 c2 test edx,eax
133: 0f 84 1e 01 00 00 je 257 <_MyFunction+0x227>
139: 81 7d d4 10 01 00 00 cmp DWORD PTR [ebp-0x2c],0x110
140: 0f 87 11 01 00 00 ja 257 <_MyFunction+0x227>
120
- 131
可以轻松跟踪,首先评估!a
,然后评估m > 0x002
。第一个跳转条件直到133
才会发生。到目前为止,已经评估了 两个 表达式,而不管第一个表达式的结果如何:!a
。如果a
等于零,表达式可以(并且应该)立即结束,这不是在这里完成的。
这与C标准有什么关系,一旦确定结果,它就要求布尔运算符短路?
答案 0 :(得分:10)
C标准仅指定“抽象机器”的行为;它没有指定程序集的生成。只要程序的可观察行为与抽象机器上的行为相匹配,实现就可以使用它喜欢的任何物理机制来实现语言结构。标准(C99)中的相关部分是5.1.2.3程序执行。
答案 1 :(得分:6)
这可能是编译器优化,因为比较整数类型没有副作用。您可以尝试编译而不进行优化或使用具有副作用的函数而不是比较运算符,并查看它是否仍然执行此操作。
例如,尝试
if (printf("a") || printf("b")) {
printf("c\n");
}
它应该打印ac
答案 2 :(得分:5)
正如其他人所提到的,这个程序集输出是一个编译器优化,不会影响程序执行(就编译器而言)。如果您想选择性地禁用此优化,您需要告诉编译器您的变量不应在代码中的 序列点 之间进行优化
序列点 是控制表达式(if
,switch
,while
,do
中的评估和for
)的所有三个部分,逻辑OR和AND,条件(?:
),逗号和return
语句。
要防止这些点之间的编译器优化,您必须声明变量volatile
。在您的示例中,您可以指定
volatile long a;
unsigned long m;
{...}
if (!a && m > 0x002 && m < 0x111) {...}
这样做的原因是volatile
用于指示编译器无法预测等效机器相对于变量的行为。因此,它必须严格遵守代码中的序列点。
答案 3 :(得分:4)
编译器的优化 - 它将结果输入EBX,将其移至AL,EAX的一部分,第二次检查EDX,然后根据EAX和EDX的比较进行分支。这样可以节省分支并使代码运行得更快,而且副作用方面根本没有任何差别。
如果使用-O0
而不是-O2
进行编译,我想它会产生更加天真的汇编,更符合您的期望。
答案 4 :(得分:3)
代码表现正确(即符合语言标准的要求)。
您似乎正在尝试找到生成特定汇编代码的方法。在两个可能的汇编代码序列中,两者的行为方式相同,你会发现一个令人满意而另一个不令人满意。
保证令人满意的汇编代码序列的唯一可靠方法是显式编写汇编代码。 gcc确实支持内联汇编。
C代码指定行为。汇编代码指定机器代码。
但所有这些都提出了一个问题:为什么这对你很重要? (我不是说它不应该,我只是不明白为什么它应该。)
编辑: a
和m
到底是如何定义的?如果您按照建议将它们与内存映射设备相关联,那么它们应该被声明为volatile
- 这可能正是您问题的解决方案。如果它们只是普通变量,那么编译器可以随心所欲地做任何事情(只要它不影响程序的可见行为)因为你没有要求它。 / p>