• 对C语言整数类型的一点理解


    作者:autogeek

    原文链接:http://www.cnblogs.com/autogeek/p/4321635.html

    1.先从一个列子引出问题:

    //sample_1 unsigned char 
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        unsigned char a = 255;
        a = a + 1;
        printf("a = %d
    ", a);
        return 0;
    }

    输出结果:

    image

    为什么是0 而不是256, 这是很显然的发生了溢出。现在从机器存储的角度来看这个问题:

    在C语言中,char表示占用一个byte空间,在机器上一个byte可存储的范围是0000 0000 b ~ 1111 1111 b, 现在不考虑符号问题,仅仅把它解释为无符号的整数,那么对应十进制范围是0 ~ 255, 现在我们的a初始值是255 即 1111 1111 b,接着给它加1,那么二进制运算后变为 1 0000 0000 b, 由于进位,结果变成了9个bit,但是别忘了char是只占一个byte的,所以最前面的那个bit 将会被丢弃,结果变成 0000 0000 b, 因此输出结果会是0.

    这个列子说明,整数在机器上存储的方法是满足二进制的运算律的,但是当用一定类型表示出来时,这个二进制的值就要按照那个类型的约束表现出来,就像上面的列子一样,即使最后的二进制结果是9个bit,但是char类型明确约束你只能占8个bit,最后只能丢弃1个bit之后按8个bit的结果展示出来。

    2.第二个列子,引入有符号类型的表示问题:

    //sample_2 signed char
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char a = 127;
        a = a + 1;
    
        printf("a = %d
    ", a);
        return 0;
    }

    输出结果:

    image

    先不忙分析问题原因,先来搞清楚负数是如何在计算机上存储的。简单的说,计算机擅长做0和1的加法,并不愿意像人类用的十进制一样去引入负号这个东西,否则会增加硬件和软件的复杂性和成本,如何解决这个问题,众所周知的就是用补码。可以举个列子去理解补码的作用,1+(?)= 0,很显然 1 + (-1)= 0, 这样就把减法问题转化为加法问题,那么在计算机中怎么去表示(-1)? 同样的,我们站在计算机的角度想问题(先拿一个byte的来举例),(0000 0001 b) + (?) = (0000 0000 b), 当然是(0000 0001 b) + (1111 1111 b) = (0000 0000 b),因此(1111 1111 b) 就是十进制1(原码)的补码,教科书中说补码可以通过对原码取反加一得到,其实通过上面的列子可以换一种角度解读补码的含义:即补码就是那个使得和原码相加之后结果能够为0的数。说了这么多,就是想说明对一个byte来说,(-1)在计算机上存储为(1111 1111 b)。

    那么对于有符号char型来说,还是一个byte的空间,还是0000 0000 b ~ 1111 1111 b的范围,但是现在要把这个范围按有符号整数的表示来翻译了,可以分为3部分来理解,即 < 0,0 ,> 0,0000 0000 b显然是0, 0000 0001 b ~ 0111 1111 b显然是 1 ~ 127, 1000 0000 b ~ 1111 1111 b 是 –1 ~ –128。由此也可以发现,可以把最高位作为符号位来理解,0正1负。

    image

    换一种表达方式 (图画的太渣了,求推荐好的画图工具)

    image

    上面这两张图希望能表达清楚我上面的分析结论,即计算机上存储的整数一定是满足二进制的运算律的,但是怎么去解释它就取决于整型变量的类型约束条件了。

    说了这么多,再来解释第二个列子就简单多了。第一步char a = 127 说明a是有符号的,127在计算机上存储为0111 1111b, 第二步 a = a + 1,a在计算机上变成了1000 0000b, 对应上面的分析,把它按照有符号数来解释即 -128,因此输出-128。

    3.上第三个列子,关于符号位扩展问题:

    //sample_3
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char a = 127;
        a = a + 1;
    
        printf("a = %u
    ", a);
        return 0;
    }

    输出结果:

    image

    我只是将sample_2的代码中%d改成了%u,输出结果就差之千里,其实还是一个道理,用不同的方式解释计算机存储的二进制值就会得到不同的结果。当然这里还涉及符号位扩展以及类型提升。

    首先说类型提升,%d表示打印int类型有符号十进制数,%u表示打印int无符号十进制数,当a被打印时隐式的发生类型提升,即char被提升为int,即8位被扩展为32位。

    对于sample_3来说:

    a的值在1个byte中,是 0111 1111b

    a加1后在1个byte中,是 1000 0000b

    a的值被类型提升并扩展为4个byte后,是11111111 11111111 11111111 10000000 b,按照%u来解释是多少呢,当然是4294967168。

    可是为什么扩展后前面的空位都是补1而不是补0呢,这个有个原则是 “有符号数扩展符号位,也就是向前补1,无符号数扩展0”。

    那么sample_2应该也发生类型提升和符号扩展,为什么没出问题呢。因为虽然a的值被类型提升并扩展为4个byte后,依然是11111111 11111111 11111111 10000000 b,按照%d来解释,他是00000000 00000000 00000000 10000000 b的补码,所以表达为-128。

    以上是自己的一些理解,难免有错误,欢迎讨论。

  • 相关阅读:
    SpringBoot自动装配
    Docker容器添加对外映射端口
    Day03 腿部训练
    Day04 胸 + 肩部训练
    Day02 背 + 二头肌
    @Transactional注解失效场景
    Docker快速安装RocketMQ
    Linux 命令 速记
    Window Phone 7 设备的方向
    使用异步代理+IAsyncResult 调用函数
  • 原文地址:https://www.cnblogs.com/autogeek/p/4321635.html
Copyright © 2020-2023  润新知