C 语言的数据类型宽度扩展
发布日期:2021-06-29 19:19:34 浏览次数:3 分类:技术文章

本文共 2230 字,大约阅读时间需要 7 分钟。

代码编译运行环境: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;	//例1char x=00001001b;   short y=x;  则y的值应为00000000 00001001b;	//例2

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

unsigned char x=10001001b;   short y=x;  则y的值应为00000000 10001001b;	//例1unsigned 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]

转载地址:https://dablelv.blog.csdn.net/article/details/50072445 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:C++ 多字节与宽字符串的相互转换
下一篇:C++ endl 的本质是什么

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月10日 04时50分10秒