Q: int a = b = 3; 为什么会编译错误?
A: 要看情况。C语言的变量必须先声明后定义。上面的表达式,如果b声明过,可以编译过,如果没有,才会编译错误。
Q:C语言为什么支持上面看起来怪怪的表达式?
A:看看C标准。
请自行脑补。
Q: int i = 3.2f; i为什么等于3 ?
A:C语言标准规定float到int, 只取整数部分,丢弃小数部分。至于i是如何被赋值为3,一般编译器会先计算出3,直接赋值给i.
也许有人会想到编译器是如何计算3.2f到3的?
一种可能是3.2f转换成IEEE 754标准的二进制数据,再从二进制数据中拿掉小数,计算出整数。这太麻烦了,编译器最直接的做法是看到3.2f是float数据,取小数点前面的3赋值给i即可。
int i = 3.2f;
printf("%d\n", i);
movl $0x3, %esi // 编译器计算出i为3,直接传入printf
xorl %eax, %eax
callq 0x100000f92
Q: 如果初始化的float数值很大,超出int范围,i会是多少?
A: 对于这种超出范围的情况,编译器采用一些滑头的做法,它们不保证正确,也许是随便放个数据进去。好吧,这年头,只要能省,什么都想着方便......
int i = 2147483648.5f;
printf("%d\n", i);
i = 2147483648;
printf("%d\n", i);
0000000100000f56 movl %edi, -0x14(%rbp) // 第一个i: %edi对应i,编译器看心情分配了%edi,数值未定
0000000100000f59 movl -0x14(%rbp), %esi
0000000100000f5c leaq 0x4f(%rip), %rdi
0000000100000f63 movb $0x0, %al
0000000100000f65 callq 0x100000f92
0000000100000f6a movl $0x80000000, -0x14(%rbp) // 第二个i: 编译器算出是0x80000000
0000000100000f71 movl -0x14(%rbp), %esi
0000000100000f74 leaq 0x37(%rip), %rdi
0000000100000f7b movl %eax, -0x18(%rbp)
0000000100000f7e movb $0x0, %al
0000000100000f80 callq 0x100000f92
Q: float f = 2; f是怎么被赋值的?
A:
float f = 2;
printf("%f\n", f);
0000000100000f6c leaq 0x3d(%rip), %rdi
0000000100000f73 movsd 0x2d(%rip), %xmm0 // 参数f放到xmm0寄存器中
0000000100000f7b movb $0x1, %al
0000000100000f7d callq 0x100000f86
0x2d(%rip)数据是:
2是如何和00 00 00 00 00 00 00 40对应的?
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
void dump_data(void *data, int bytes)
{
int i;
unsigned char *p;
p = (unsigned char *)data;
for (i = 0; i < bytes; ++i) {
printf("%x ", p[i]);
}
printf("\n");
}
int main(int argc, char *argv[])
{
float d;
scanf("%f", &d);
dump_data(&d, sizeof(d));
return 0;
}
可以看到float确实是通过IEEE 754做内部存储的。可参考:精确度(你想知道的C语言 3.8)
Q:长数据类型到短数据类型是如何转换的?
A: 以int转换成short为例,默认都是截取低字节直接赋值。
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
short h;
scanf("%d", &i);
h = i;
printf("%hd\n", h);
return 0;
}
leaq 0x57(%rip), %rdi
leaq -0x10(%rbp), %rbx // i的地址
xorl %eax, %eax
movq %rbx, %rsi
callq 0x100000f86
movl (%rbx), %esi // 将rbx对应的8字节转换成4字节放到esi中
leaq 0x44(%rip), %rdi
xorl %eax, %eax
callq 0x100000f80
作者: 陈曦
环境: MacOS 10.14.5 (Intel i5)
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Linux 3.16.83 (Ubuntu)
转载请注明出处