前不久和同事谈论起 unsigned 关键字,故今天小结一下。
以32位机为例,int 分为无符号 unsigned 和有符号 signed 两种类型,默认为signed。二者的区别就是无符号类型能保存2倍于有符号类型的数据。32位下,signed int 的表示范围为:-2147483648 ~ 2147483647 (最高位做符号位)。unsigned int 的表示范围为:0 ~ 4294967295 (不保留符号位)。我们都知道,两个不同的数据类型在进行混合使用时,会自动进行类型转换。其转换原则就是:向着精度更高、长度更长的方向转换。也就是我们平常见到的 char 转为 int ,int 转为 long,float 转为 double . etc. 那么当涉及到unsigned 类型时,又会进行怎样转换呢?下面来看个小例子:
#include
#include
int main()
{
int arr[]={1,2,3,4};
if(-1
下面我们看一下它的反汇编:(不想看的童鞋可以跳过)
int main()
{
00F31690 push ebp
00F31691 mov ebp,esp
00F31693 sub esp,0DCh
00F31699 push ebx
00F3169A push esi
00F3169B push edi
00F3169C lea edi,[ebp-0DCh]
00F316A2 mov ecx,37h
00F316A7 mov eax,0CCCCCCCCh
00F316AC rep stos dword ptr es:[edi]
00F316AE mov eax,dword ptr [___security_cookie (0F37000h)]
00F316B3 xor eax,ebp
00F316B5 mov dword ptr [ebp-4],eax
int arr[]={1,2,3,4};
00F316B8 mov dword ptr [ebp-18h],1
00F316BF mov dword ptr [ebp-14h],2
00F316C6 mov dword ptr [ebp-10h],3
00F316CD mov dword ptr [ebp-0Ch],4
if(-1
注意一下第22,23行,xor eax,eax 异或运算的结果为0,所以总会跳转至0F316F1h。执行Error语句。
下面我们再修改一下这个程序:
#include
#include
int main()
{
int arr[]={1,2,3,4};
if((double)-1
注意,这次我们在if语句里,将-1转换成了double类型。编译运行一下:hello world 。我们想要的结果终于出来了,在我们欣喜的同时,不禁要问:why ?
我们再来看一下它的汇编:
int main()
{
00BE1690 push ebp
00BE1691 mov ebp,esp
00BE1693 sub esp,0DCh
00BE1699 push ebx
00BE169A push esi
00BE169B push edi
00BE169C lea edi,[ebp-0DCh]
00BE16A2 mov ecx,37h
00BE16A7 mov eax,0CCCCCCCCh
00BE16AC rep stos dword ptr es:[edi]
00BE16AE mov eax,dword ptr [___security_cookie (0BE7000h)]
00BE16B3 xor eax,ebp
00BE16B5 mov dword ptr [ebp-4],eax
int arr[]={1,2,3,4};
00BE16B8 mov dword ptr [ebp-18h],1
00BE16BF mov dword ptr [ebp-14h],2
00BE16C6 mov dword ptr [ebp-10h],3
00BE16CD mov dword ptr [ebp-0Ch],4
if((double)-1
注意第22,23,24行,test eax, eax 与运算的结果为1,je不跳转,就继续执行了hello world。
如此看来,是编译器在编译源代码的时候,做了一些我们不知道的工作。查一下文档,找到这么一段:
ANSI C 标准采用值保留(value preserving)原则,就是当把几个整型操作数混合使用时,其结果的类型可能是有符号数,也可能是无符号数,这取决于操作数的类型的相对大小。(通俗点说,就是两个整型数,如果都转换为signed不会丢失信息,就转换为signed;否则就转换为unsigned。)
在例一中,if(-1 在例二中,我们将-1先转换成了8个字节的double类型。然后再和4个字节的unsigned类型比较时,由于double完全容得下unsigned。所以,此时,操作数都转换为有符号的double类型。所以 if( (double)-1 < (double)1) 成立。此时,就输出 hello world了。 总结:尽量不要在你的代码中使用无符号类型,以免增加不必要的复杂性;或者使用时,在涉及混合运算时,进行强制类型转换,这样就不必由编译器来选择转换类型了。 ------------------------------------------------------------------------------------------------------ 补充一点: 1,ANSI C 标准中采用的是值保留原则。而K&R C 标准中,采用的是无符号保留(unsigned preserving)原则,即:不区分操作数大小,统一转换为无符号类型。如果采用K&R C 标准的话,那么例一、例二就都会输出Error了。 2,当前编译器基本都遵循ANSI C 标准。如:gcc默认GNU C。 即:GNU C = C90 + GNU Extensions = C90 + (some features in C99 + some features in C11)