好的,所以一切都从这里开始:Unsigned integer and unsigned char holding same value yet behaving differently why?
我编写了以下应用程序来了解幕后发生的事情(即编译器如何处理这个问题)。
#include <stdio.h>
int main()
{
{
unsigned char k=-1;
if(k==-1)
{
puts("uc ok\n");
}
}
{
unsigned int k=-1;
if(k==-1)
{
puts("ui ok");
}
}
}
在用GCC编译时,如:
gcc -O0 -S -masm=intel h.c
我得到以下程序集文件:
.file "h.c"
.intel_syntax noprefix
.section .rodata
.LC0:
.string "ui ok"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 16
mov BYTE PTR [rbp-1], -1
mov DWORD PTR [rbp-8], -1
cmp DWORD PTR [rbp-8], -1
jne .L3
mov edi, OFFSET FLAT:.LC0
call puts
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
令我惊讶的是,第一次检查并非偶然。
但是,如果我用Microsoft Visual C ++(2010)编译相同的东西,我得到(我已经从这个列表中切掉了很多垃圾,这就是为什么它不那么有效):
00B81780 push ebp
00B81781 mov ebp,esp
00B81783 sub esp,0D8h
00B81789 push ebx
00B8178A push esi
00B8178B push edi
00B8178C lea edi,[ebp-0D8h]
00B81792 mov ecx,36h
00B81797 mov eax,0CCCCCCCCh
00B8179C rep stos dword ptr es:[edi]
00B8179E mov byte ptr [k],0FFh
00B817A2 movzx eax,byte ptr [k]
00B817A6 cmp eax,0FFFFFFFFh
00B817A9 jne wmain+42h (0B817C2h)
00B817AB mov esi,esp
00B817AD push offset string "uc ok\n" (0B857A8h)
00B817B2 call dword ptr [__imp__puts (0B882ACh)]
00B817B8 add esp,4
00B817BB cmp esi,esp
00B817BD call @ILT+435(__RTC_CheckEsp) (0B811B8h)
00B817C2 mov dword ptr [k],0FFFFFFFFh
00B817C9 cmp dword ptr [k],0FFFFFFFFh
00B817CD jne wmain+66h (0B817E6h)
00B817CF mov esi,esp
00B817D1 push offset string "ui ok" (0B857A0h)
00B817D6 call dword ptr [__imp__puts (0B882ACh)]
00B817DC add esp,4
00B817DF cmp esi,esp
00B817E1 call @ILT+435(__RTC_CheckEsp) (0B811B8h)
问题是:为什么会这样?为什么海湾合作委员会“跳过”第一个IF,我怎么能强迫GCC不跳过它?优化被禁用,但它似乎仍然优化了一些东西......
答案 0 :(得分:7)
我的猜测(我不是GCC开发人员)是它做了足够的静态分析来向自己证明第一个if
的测试永远不会成真。
这不应该太很难,因为初始化和测试之间没有代码,任何副作用或外部实体都无法改变变量。
只是出于好奇,尝试制作变量static
和/或volatile
以查看是否有任何变化。
答案 1 :(得分:2)
这看起来像海湾合作委员会的一个问题,虽然这是一个非常小的问题。
来自GCC's documentation website(强调我的):
如果没有任何优化选项,编译器的目标是降低编译成本并使调试产生预期结果。 语句是独立的:如果在语句之间使用断点停止程序,则可以为任何变量分配新值或将程序计数器更改为函数中的任何其他语句并获得完全结果期待源代码。
因此,对于-O0
,您应该能够在unsigned char k=-1;
和if(k==-1)
之间放置一个断点,在该断点期间修改k
,并期望采取分支;但这对于发出的代码是不可能的。
答案 2 :(得分:1)
更新:我的猜测是,char(作为base(int)类型下面的类型)只是放大到整数类型进行比较。 (假设编译器将文字作为整数,并且通常更喜欢字大小的整数而不是字节大小的整数)
作为无符号值,零扩展始终为正(请注意 MOVZX 而不是带符号的变体!),因此检查可能会被基本的常量传播优化掉。
您可以尝试强制文字为字节(强制转换或后缀),例如与((unsigned char)( - 1))比较,然后编译器可能会插入一个1字节的比较,结果可能会有所不同。
答案 3 :(得分:0)
这里有很多细节:
00000000 00000000 00000000 xxxxxxxx
。常量-1具有位模式11111111 11111111 11111111 11111111
,因此xxxxxxxx
无关紧要,比较结果将始终为假。