0x2a

Don't Panic.

Steve's avatar Steve

C语言中将-1赋值给unsigned类型变量

前因

上一周C语言实验作业中有一道这样的思考题

问题

代码

然后我的答案是这样的,当时也没有细想。

short int 类型占两个字符 也就是十六位二进制保存数据 取值范围是-32768~+32767.

而unsigned short 类型没有符号 故取值范围为0~65535.

计算机使用补码存储数据 故short int b = -1存储到计算机里面的数据是1111 1111 1111 1111

让b的值赋给a 则a此时也是1111 1111 1111 1111 1111但是a的类型是unsigned short 没有符号位 所以用%d格式化输出则为65535

然后很开心交了作业。结果舍友昨晚问我的时候我复制粘贴了一下,舍友回复了我一张图。

百度

emmmm…溢出???unsigned short a之后就不能赋值为负数?

但是后来发现百度知道上面的题目是int b = -1 而作业里面是 short int = -1, -1也在short int 的范围内 应该不会溢出。

但是还是举得自己的基础知识太薄弱,决定好好研究一下这个问题。

分析

先前了解到,计算机是用补码 储存信息的。所以分析代码:

unsigned short a;
short int b = -1;

定义了一个unsigned类型的a ,以及一个short类型的b 且赋予初值 -1.

根据C语言的基础数据类型可知 short int 类型大小为2bytes,则此时内存中b储存的数值为1111 1111 1111 1111(-1的补码)。

下一步:

a = b;

而C语言的赋值语句是将变量在内存中的数据赋值到另一个变量中而不考虑对应变量的数据类型的。

引用 【不周山之读薄 CSAPP】壹 数据表示 中一段话就是

  • 在进行有符号和无符号数的互相转换时:

    • 具体每一个字节的值不会改变,改变的是计算机解释当前值的方式
    • 如果一个表达式既包含有符号数也包含无符号数,那么会被隐式转换成无符号数进行比较

此时a 变量在内存中的数据依旧是 1111 1111 1111 1111

而a 的数据类型为unsigned short,大小依然是2bytes。a为无符号类型,则不用考虑第一位用作储存符号。则a =1111 1111 1111 1111(2)= 65535(10)

为了验证不是溢出等问题,稍作修改代码:

int main(void)
{
    unsigned short a;
    short int b = -2;
    a = b;
    printf("%d\n",a);
    return 0;
}

此时输出的值为65534

-2 的补码为1111 1111 1111 1110 转换为unsigned short的a就是十进制的65534 故得证。

扩展

当代码改成这样时:

int main(void)
{
    unsigned int a;
    int b = -1;
    a = b;
    printf("%u\n",a);
    return 0;
}

输出的结果为 4294967295。

需要注意的是这里输出格式换成了%u,如果像一开始那样使用%d格式化输出会依然输出-1,是因为%d输出的就是有符号的整型。

C语言输出不会因为变量一开始定义了变量类型输出时就会按那种类型输出 还是要使用%进行格式化输出。

(一个疑问,为什么一开始的代码可以使用%d而不是使用最标准的%hu也可以输出我们想要的结果)

回到正题,同原始问题一致的分析方法。

int在32位以上系统是4bytes(TC时代的就别来祸害人了),因此此时的b在内存中的数据为 1111 1111 1111 1111 1111 1111 1111 1111(32个1),将b的值赋给a,而a是无符号类型,则a %u格式化输出为2^32 - 1 =4294967295.

这是一篇学艺不精产生的水博文(叹气

以后还是要多多关注基础性的知识。

加油把《深入理解计算机系统》啃完!