本文借鉴自https://blog.csdn.net/simple_the_best/article/details/48421279,https://blog.csdn.net/zhangzhi123456789/article/details/49589137,感谢原作者的贡献!

1. c语言数据底层存储

众所周知,c语言的基本数值数据类型在底层都是用补码进行存储的。

先写一个将十进制int类型转换为二进制再输出的函数方便后续测试:

char* Decimal2Binary(int num) {
    const int length = 8 * sizeof(int);
    char* ret = (char *)malloc(sizeof(int));
    int i = length - 1;
    for(; i >= 0; i--, num >>= 1)
        ret[i] = (1&num) + '0';
    ret[length] = '\0';
    return ret;
}
// Test
char* ret = Decimal2Binary(-1);
printf("%s\n", ret);
// Output:
// 11111111111111111111111111111111

由上面测试可知,-1在计算机底层的存储形式为全1,符合上面所说的补码存储。

2. 同等长度变量之间类型转换

首先,对于int、char等可以被unsigned修饰的数据类型来说,在创建变量时无论加不加unsigned其实数据在底层存储的内容是一样的,就像你可以给unsigned int赋值-1,编译器并不会报错,而这个数据在底层同样是以全1存储的。

同样长度不同类型相互转换时,比如int强制转换为unsigned int。实际在转换过程中,转换后的unsigned int和原来的int在计算机中存储的二进制内容不变,改变的其实是对这串二进制的解释方式

代码测试:

int i = -1;
printf("%s\n", Decimal2Binary(i));
printf("signed: %d\n", i);
printf("unsigned: %u\n", i);
// Output:
// 11111111111111111111111111111111
// signed:-1
// unsigned:4294967295 

3. 长字长变量向短字长变量的转换

系统会将多余的高位字长部分直接截断,低位原封不动赋值。
这时候就需要注意了,这种转换最好永远不要用到,因为直接截断这种粗暴的处理方式很大可能会输出你不想要的结果,对于有符号数来说也很容易连符号都被改变。

测试代码:

printf("%s\n", Decimal2Binary(-43211));
printf("%hd\n", -43211);
// Output:
// 11111111 11111111 01010111 00110101
// 22325
// 22325即二进制01010111 00110101

4. 短字长变量向长字长变量的转换

短字长转向长字长
当短字长数据为unsigned类型时,高位部分全部填充0。
当短字长数据为signed类型时,高位部分扩展为原数据符号位。

测试代码:

unsigned short s = -1;
signed short ss = -1;

printf("%s\n", Decimal2Binary((int)s));
printf("%s\n", Decimal2Binary((int)ss));
ss = 1;
printf("%s\n", Decimal2Binary((int)ss));
// Output:
// 00000000000000001111111111111111
// 11111111111111111111111111111111
// 00000000000000000000000000000001

5. 总结

数值类型转换分三种情况:同等长度变量之间的转换、长字节变量转换为短字节变量、短字节变量转换为长字节变量。

在变量字长发生变化的转换中,可以看作先将原数据根据对应规则转换成所需字长,再根据转换后的前缀signed/unsigned来判断如何对这串二进制进行解释。

Last modification:November 10th, 2019 at 10:10 am