float n1=3.0;
double n2=3.0;
long n3=2000000000;
long n4=1234567890;
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
在x86 32位机上执行,输出结果是:0 1074266112 0 1074266112
按照我的理解,float和long在32位机下都占4个字节,即使无法格式化为long进行输出,后面的值也不至于都出问题。
实际里面涉及以下的知识,
1. 把上面的代码反汇编可以看到:
mov 0x80494b4,%eax
mov %eax,0x18(%esp)
mov 0x80494b0,%eax
mov %eax,0x14(%esp)
fldl 0x80494a8
fstpl 0xc(%esp)
flds 0x80494a0
fstpl 0x4(%esp)
movl $0x8048478,(%esp)
我先后把这几个变量定义成了局部和全局的进行了测试,这段是用全局的汇编码。
关键是这几句:
fldl 0x80494a8
fstpl 0xc(%esp)
flds 0x80494a0
fstpl 0x4(%esp)
fldl把浮点地址交给浮点运算寄存器,浮点寄存器是64为精度的,然后fstpl再把值取出按照64位入栈,表象就是按照double类型入栈了
。所以虽然内存中单精度浮点数是以4字节存储的,但编译器把它转成了8字节进行处理。
所以最后在栈里的存储就成了
| 00 00 00 00 |<--ESP
| 40 40 00 00 |
| 00 00 00 00 |
| 40 40 00 00 |
| 49 96 02 d2 |
| 77 35 94 00 |<--EBP
2. printf的处理方式是将输入参数按照字符串进行格式化解析,由于输入的是ld ld ld ld,按照4字节依次进行取值,就得到了上面的结果。
3. float是单精度浮点,遵从IEEE754, S(1)-E(8)-M(23)。3二进制位11,表示为:1.1×2^1 => 指数部分127+1=128-
>10000000,小数部分为1,因此其浮点表示为:
0 10000000 10000000000000000000000,也就是上面1中看到的0x40400000,打印出的十进制就是1074266112。
后来又找来台cavium64位的机器上跑了一下,发现都是正确的,即使按照%d方式输出也没有问题,没法在cavium上反汇编无法确认。