C 语言的数据类型宽度扩展

代码编译运行环境:Windows 64bits+VS2017+Debug+Win32


1.问题描述

在编程或者面试过程中,关于数据类型宽度的扩展,可能会遇到如下问题:

char c=128;
printf("%d",c); //输出-128

为什么一个正整数 128 以整型 int 输出时变成了一个负数?

2.问题分析

在理解上面的问题时,我们需要先了解如下问题。

char 型所能表示的数据范围是 -128~127。当把 128 赋值给 char 型变量时,那么内存中实际存储的是什么呢?将以上面的代码在 Debug 模式下转到反汇编,汇编代码如下:

	char c=128;
00B16AB0  mov         byte ptr [c],80h  
	printf("%d",c);
00B16AB4  movsx       eax,byte ptr [c]  
00B16AB8  mov         esi,esp  
00B16ABA  push        eax  
00B16ABB  push        0B1EC90h  
00B16AC0  call        dword ptr ds:[0B2240Ch]  
00B16AC6  add         esp,8  
00B16AC9  cmp         esi,esp  
00B16ACB  call        __RTC_CheckEsp (0B113CFh)  

从汇编代码可以看出,char 型变量 c 中存储的是 128 的补码,也是 128的原码:10000000b。注意对于计算机来说,整型数值存储的都是补码,而反码、原码是为了方便编程人员理解数据的变换而提出来的。

当 char 转换为 int 时,内存中的数据如何从 1 个字节扩展到 4 个字节?这个是本文的核心问题,理解了这个,就可以很好地解释为什么char c=128;printf("%d",c)输出的是 -128。

当 char 型扩展到 int 型时,C 标准中有如下规则:

(1)短数据类型扩展为长数据类型。
(a)要扩展的短数据类型为有符号数,进行符号扩展,即短数据类型的符号位填充到长数据类型的高字节位(即比短数据类型多出的那一部分),保证扩展后的数值大小不变

char x=10001001b;	short y=x;  则y的值应为11111111 10001001b;	//例1
char x=00001001b;   short y=x;  则y的值应为00000000 00001001b;	//例2

(b)要扩展的短数据类型为无符号数,进行零扩展,即用零来填充长数据类型的高字节位。

unsigned char x=10001001b;   short y=x;  则y的值应为00000000 10001001b;	//例1
unsigned char x=00001001b;   short y=x;  则y的值应为00000000 00001001b;	//例2

(2)长数据类型缩减为短数据类型。
如果长数据类型的高字节全为 1 或全为 0,则会直接截取低字节赋给短数据类型;如果长数据类型的高字节不全为 1 或不全为 0,则转换就会发生错误。

(3)同一长度的数据类型中有符号数与无符号数的相互转化。
直接将内存中的数据赋给要转化的类型,数值大小则会发生变化,因为以不同类型解释同一段内存数据会得到不同的数值。比如一个字节中存放的数据是 1111 1111b,以 unsigned char 来解释是 255,以 char 来解释是 -1。

根据以上规则,可以得出当 char c 是一个有符号的字符变量,其内存中存储的是 1000 0000,但当它被传送到 printf() 函数的参数时,是将 c 按照 int 来进行宽度扩展后再传给 printf()。

128 的补码是 1000 0000b,16 进制是 0x80,当它扩展为 int 时,由于 int 是 4 个字节,需要进行短数据类型扩展到长数据类型。由于内存中存放的是 10000000,以 char 型来解释的话第一位为符号位,表示负数,进行符号扩展为 int 后,int 型变量中存储的数据是:11111111 11111111 11111111 1000000,即 0xffffff80。以 int 来解释的这四个字节的数据,其值就是 -128,以 unsigned int 来解释的话,就是 2 32 − 1 − 127 = 4294967168 2^{32}-1-127=4294967168 2321127=4294967168

3.代码验证

根据以上分析,我们可以清楚准确地推断出下面的输出。

	unsigned char uc=128;
	char c=128;
	printf("%d\n",uc);		//128
	printf("%d\n",c);		//-128
	printf("%u\n",uc);		//128
	printf("%u\n",c);		//4294967168
	printf("%08x\n",uc);	//0x00000080
	printf("%x\n",c);		//0xffffff80

应该不会为这些输出结果而感到惊讶和困惑了吧!


参考文献

[1] 类型扩展
[2] char c=128

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值