问题产生
在进行short int类型的%x输出时,注意到输出的结果相比规范的%hx有一些差异,于是初步有两种猜想:1.使用%x输出short int的内容时发生了内存泄漏;2.%x对short int类型进行了类型转换,在验证这一问题的基础上又对写入的十六进制被视作原码还是补码产生了疑问,于是有了以下探究。
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
short int num = 0xa001;
printf("%x\n%hx\n", num, num);
system("pause");
return 0;
}
显示内存的函数
这里自定义了两个函数用于显示计算机中数据的二进制表示。值得注意的是位运算是对补码进行的运算,而运算结果为0的情形只有所有位均为0的情形,这是这两个函数的理论基础。
void showShortBit(short num)
{
int mask, i;
mask = 1 << 15;
for(i=1; i<=16; i++)
{
putchar(num&mask ? '1' : '0');
num <<= 1;
if( i%4==0 ) putchar(',');
}
printf("\bB\n");
}
void showIntBit(int num)
{
int mask, i;
mask = 1 << 31;
for(i=1; i<=32; i++)
{
putchar(num&mask ? '1' : '0');
num <<= 1;
if( i%4==0 ) putchar(',');
}
printf("\bB\n");
}
验证过程
十六进制写入与读取
short int a = 0xa001;
short int b = -1;
showShortBit(a);
showShortBit(b);
printf("%hx\n%hx\n", a, b);
由此可见,以十六进制写入时,直接被视为补码;以%x输出时,直接输出补码。
类型提升
short int a = 0xa001;
unsigned short b = 0xa001;
short int c = 0x6001;
showShortBit((a));
showShortBit(b);
showShortBit(c);
showIntBit((int)a);
showIntBit((int)b);
showIntBit((int)c);
printf("%hx\n%hx\n%hx\n", a, b, c);
printf("%x\n%x\n%hx\n", a, b, c);
由此可知,在进行类型提升时,高位补0还是1取决于原类型的符号类型。如果原类型为无符号,则补码高位补0;原类型为有符号,补码高位按照符号位进行补全。
这一底层原理在宏观上很容易理解,类型提升操作应保持数值大小不变(原码)。
问题解决
解决最初问题的思路较为简单,给定一段已知的内存即可验证是否发生了内存泄漏,这里采用union的方法。
union Disk
{
short int b;
char c[2];
} disk;
disk.b = 0xa001;
showShortBit(disk.b);
printf("b:%x \nc:%x %x\n", disk.b, disk.c[0], disk.c[1]);
可以从c[0]的结果中看到,输出int长度的char类型时,并没有读取前面的内存,只是进行了类型的提升。
ps:真实的探究过程是从下往上进行的,这里为了逻辑的严密从上往下写。
总结
1.使用%x输出%hx的内容时,会进行类型提升(转换),对于其他情形,如%hx输出%x的内容,可自行验证。
2.以0x方式赋值变量时,原封不动的将十六进制数存储,不管是否为有符号类型;而进行类型转换时,若为有符号数,又根据最高位确定符号位。
3.以%x方式输出数据时,输出的是内存中的补码,即计算机中真实的存储方式,不进行原码的转化。