解开初始化语句的“迷雾重重“(你想知道的C语言 4.1)

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)
 
转载请注明出处

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值