下文所有说法仅针对linux等long类型在32位和64位程序中,长度不一样的系统成立,因为WIndows系统long不论是32位还是64位,都是4字节。
下面的程序在32位和64位下,会输出什么结果?
1 #include <stdio.h>
2
3 int main()
4 {
5 long la = -1l;
6 unsigned int uib = 0u;
7
8 if(la<uib)
9 {
10 printf("-1l<0u\n");
11 }
12 else
13 {
14 printf("-1l>0u\n");
15 }
16 return 0;
17 }
在Linux系统使用GCC编译,运行,结果如下:
$ gcc cmp.c -m32 -g -o cmp32
[nereus@nereusp cpp]$ ./cmp32
-1l>0u
$ gcc cmp.c -g -o cmp64
$ ./cmp64
-1l<0u
可以发现在32位程序中,出现了-1L>0U的情况,为什么呢?
还是先来看看汇编吧,为了让汇编尽可能的简单,我把代码中的stdio部分去除了,修改后的代码如下:
1 int main()
2 {
3 long la = -1l;
4 unsigned int uib = 0u;
5
6 int c = la<uib;
7 return 0;
8 }
32位程序反汇编:
080483db <main>:
int main()
{
80483db: 55 push %ebp
80483dc: 89 e5 mov %esp,%ebp
80483de: 83 ec 10 sub $0x10,%esp
long la = -1l;
80483e1: c7 45 fc ff ff ff ff movl $0xffffffff,-0x4(%ebp)
unsigned int uib = 0u;
80483e8: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp)
int c = la<uib;
80483ef: 8b 45 fc mov -0x4(%ebp),%eax
80483f2: 3b 45 f8 cmp -0x8(%ebp),%eax
80483f5: 0f 92 c0 setb %al
80483f8: 0f b6 c0 movzbl %al,%eax
80483fb: 89 45 f4 mov %eax,-0xc(%ebp)
return 0;
80483fe: b8 00 00 00 00 mov $0x0,%eax
}
64位程序反汇编:
00000000004004d6 <main>:
int main()
{
4004d6: 55 push %rbp
4004d7: 48 89 e5 mov %rsp,%rbp
long la = -1l;
4004da: 48 c7 45 f8 ff ff ff movq $0xffffffffffffffff,-0x8(%rbp)
4004e1: ff
unsigned int uib = 0u;
4004e2: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp)
int c = la<uib;
4004e9: 8b 45 f4 mov -0xc(%rbp),%eax
4004ec: 48 3b 45 f8 cmp -0x8(%rbp),%rax
4004f0: 0f 9f c0 setg %al
4004f3: 0f b6 c0 movzbl %al,%eax
4004f6: 89 45 f0 mov %eax,-0x10(%rbp)
return 0;
4004f9: b8 00 00 00 00 mov $0x0,%eax
}
从上面的汇编可以发现,
在32位程序中,-1L和0U比较的时候,是先把long转换成unsigned int再进行比较的,所以是对无符号的0xFFFFFFFF和0x00000000进行比较。
在64位程序中,-1L和0U比较的时候,是先把unsigned int转换成long再进行比较的,所以是对无符号的0xFFFFFFFF和0x00000000进行比较。
比较规则如下:
if(两者长度不一致)
{
/* 将长度小者转换成长度大者的数据类型 */
}
if(有符号和无符号数比较)
{
/* 将有符号数转换成无符号数 */
}
/ * 进行比较 */
在32位系统下,比较过程如下:
(1)先判断-1L和0U的长度,都是4字节,不转换;
(2)判断符号类型,-1L是有符号,0u是无符号,将-1L转换成无符号数,0xFFFFFFFF;
(3)进行比较,无符号数0xFFFFFFFF和0x00000000比较,前者大。
在64位系统下,比较过程如下:
(1)先判断-1L和0U的长度,0U是4字节,-1L是8字节,不一致,将0U转换为长度更大者long类型;
(2)判断符号类型,此时两者都是long类型,都是有符号数,不转换;
(3)进行比较,有符号数0xFFFFFFFF FFFFFFFF和0x00000000 00000000比较,后者是正数,后者大。
Q:怎样避免这种情况?
答:比较大小之前,一定要将两被比较数类型转换成同一类型!
对于上面的代码,只要进行如下修改,就能避免这种情况:
把uib转换成long再比较
1 #include <stdio.h>
2
3 int main()
4 {
5 long la = -1l;
6 unsigned int uib = 0u;
7
8 if(la<((long)uib)) //把uib转换成long再比较
9 {
10 printf("-1l<0u\n");
11 }
12 else
13 {
14 printf("-1l>0u\n");
15 }
16 return 0;
17 }
修改之后,不论32为还是64位,输出都是
$ gcc cmp.c -o cmp32 -g -m32
$ ./cmp32
-1l<0u
$ gcc cmp.c -o cmp64 -g
$ ./cmp64
-1l<0u
$