• 学C的第二十二天【深度剖析数据在内存中的存储:1. 数据类型介绍;2. 整型在内存中的存储】


    =========================================================================

    相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

    =========================================================================

    接上期:
    学C的第二十一天【练习、补充知识点】-CSDN博客

    =========================================================================

                   

    1. 数据类型介绍

    (1). 基本的内置类型(C语言自带类型):

               

    char        --        字符数据类型        --        1字节

                

    short        --        短整型        --        2字节

                

    int        --        型        --        4字节

                

    long        --        长整型        --        4字节 或 8字节

    sizeof(long)        >=        sizeof(int)

                

    long long        --        更长的整型        --        8字节

                

    float        --        单精度浮点数        --        4字节

                

    double        --        双精度浮点数        --        8字节

               

                           

    类型的意义:

               

    1. 使用这个类型开辟内存空间的大小(大小决定了使用范围

                       

    2. C语言规定了:sizeof(long) >= sizeof(int),所以long的大小可以是4字节8字节

                  

    3. 把整型分为短整型、整型和长整型的原因:有些整型数据可能比较小,使用short就够了,如:年龄。short的范围:-32768 ~ 32767

                            

    4. 对于整型类型,还分为 有符号(signed) 无符号(unsigned)

                   

                   

    (2). 类型的基本归类:

                  

    整型类型:(只有整型分有符号和无符号)

    char

    unsigned char

    signed char

    (字符存储时,存储的是ASCII码值,是整型,所以归类时把char放在整型类型中)

    (直接写成 charsigned char 还是 unsigned char不确定的,取决于编译器)

             

    short

    unsigned short [int]

    signed short [int]        ==        short [int]

    (    [int]通常会省略掉    )

             

    int

    unsigned int

    signed int        ==        int

                 

    long 

    unsigned long [int]

    signed long [int]        ==        long [int]

    (    [int]通常会省略掉    )

              

    long long 

    unsigned long long [int]

    signed long long [int]        ==        long long [int]

    (    [int]通常会省略掉    )

                      

    signed:二进制位的最高位符号位其它位都是 数值位

    unsigned:二进制位的最高位也是 数值位,即所有位都是 数值位

                 

                 

    例如(其它整型类型以此类推):

                 

    signed char 范围是:-128~127补码:10000000无法转换为原码,被直接当成-128

                   

    unsigned char 范围是:0~255,8位全是数值位,无负数

                

                   

    浮点数类型:

    float

                

    double

              

    long double

               

    构造类型(自定义类型):

                

    数组类型:

               

    数组的元素个数数组的类型 发生变化时,数组类型就不一样了:)

    int arr1[10];        类型是        int [10]

    int arr2[5];        类型是        int [5]

    char arr3[5];        类型是        char [5]

    (这是三个不同的数组类型)

              

               

    结构体类型 struct

           

    枚举类型 enum

          

    联合类型 union

               

                   

    指针类型:

    int* pi;

               

    char* pc;

                 

    float* pf;

               

    void* pv;

                   

                 

    空类型:

    void 表示 空类型无类型):通常应用于函数的返回类型函数的参数指针类型

                              


                                 

    2. 整型在内存中的存储

    变量的创建时要在内存中开辟空间的,空间的大小根据不同的类型而决定

                 

    而开辟空间后,数据在所开辟内存中是如何存储的呢?

                   

                    

    (1). 整数用二进制表示的三种表示形式:原码、反码、补码

                 

                     

    原码:

    正数:直接数值按照正负数的形式翻译二进制得到原码

                  

    负数:直接数值按照正负数的形式翻译二进制得到原码

    或者

    反码按位取反得到原码

    再或者

    补码按位取反再+1得到原码

                 

    反码:

    正数:原码、反码、补码 都相同

              

    负数:原码符号位不变,将其它位依次按位取反得到反码

    或者

    补码-1得到反码

              

    补码:

    正数:原码、反码、补码 都相同

    负数:反码+1得到补码

                 

                  

    (2). 符号位 和 数值位(整数)

                 

    上面三种表示形式都有 符号位 和 数值位 两部分:

                   

    符号位:

    二进制最高位的一位叫做符号位

    符号位 用 0 表示 “”;

    符号位 用 1 表示 “”。 

               

    数值位:

    除了符号位,其它位都是数值位

    对于正数原码反码、补码 相同

    对于负数三种表示方法各不相同(参考上面)

              

    (演示代码:)

    1. #include
    2. int main()
    3. {
    4. int num = 10;//创建一个叫num的整型变量,这时num向内存申请4个字节来存放数据
    5. // 4个字节 - 32比特位
    6. //00000000000000000000000000001010 -- 原码
    7. //00000000000000000000000000001010 -- 反码
    8. //00000000000000000000000000001010 -- 补码
    9. int num2 = -10;
    10. //10000000000000000000000000001010 -- 原码
    11. //11111111111111111111111111110101 -- 反码
    12. //11111111111111111111111111110110 -- 补码
    13. return 0;
    14. }

                 

                      

    对于整型来说:数据存放在内存中其实存放的是补码

               

    计算机系统中,数值一律用 补码表示存储

    原因在于:使用补码,可以符号位数值位统一处理把符号位也看成数值位来计算

    同时,加法减法也可以统一处理CPU只有加法器),

    此为,补码原码相互转换,其运算过程是相同的

    原码转换为补码按位取反再+1补码转换为原码也可以按位取反再+1),

    不需要额外的硬件电路

                   

    只有加法器计算减法时1 - 1 --> 1 + (-1)

    假设计算用的是原码,算出来的是 -2,是错误

    而用补码进行计算后,再用原码表示,结果则是

            

    (为什么会倒着存储呢?)

                 

                   

    (3). 大小端介绍

                      

    字节序:

    字节单位讨论存储顺序大端字节序存储 / 小端字节序存储

                      

    低位 / 高位:

    十进制:数字123,1是百位,2是十位,3是个位。这里的1就是高位,3就是低位。

    十六进制0x 11 22 33 44,这里 11 就是高位44就是低位

                      

    大端字节序存储:

    大端(存储)模式:指数据的低位字节内容保存在内存的高地址中,而数据的高位字节内容,保存在内存的低地址中;

    低位高地址高位低地址

                

    小端字节序存储:

    小段(存储)模式:指数据的低位字节内容保存在内存的低地址中,而数据的高位字节内容,保存在内存的高地址中;

    低位低地址高位高地址

                

    为什么有大端和小段:

    一个数据只要超过一个字节,在内存中存储的时候就必然涉及到顺序的问题,所以要有大端小端存储模式对该数据进行排序

                    

    为什么会有大小端模式之分,是因为在计算机系统中,我们是以字节为单位的,每个地址单元对应一个字节一个字节8bit 。但是在C语言中除了 8bitchar 之外,还有 16bit short 类型,32位 long 类型(具体要看编译器),另外,对于位数大于8位的处理器,例如 16位 或者 32位处理器,由于寄存器宽度大于一个字节,那么就存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式小端存储模式 。

                  

    例如:一个 16bit short 类型 x ,在内存中的地址0x0010x 的值0x1122,那么 0x11 高字节0x22低字节。对于大端模式,就将 0x11 放在低地址中,即地址 0x0010 中, 0x22 放在高地址中,即地址 0x0011 中。小端模式相反

              

    我们常用的 x86 结构小端模式,所以上面的图数据会“倒着放”低位字节放在了低地址高位字节放在了高地址。而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择为大端模式还是小端模式。

                   

                 

    (4). 写个程序判断大小端:

                   

    思路:

    变量a存放在内存中的十六进制数为:01 00 00 00(小端存储),地址第一位是1

    如果是大端存储:则应该是:00 00 00 01,地址第一位是0

    可以把 a 的地址取出第一位,如果

    第一位地址 == 1,说明是小端存储

    第一位地址 == 0,说明是大端存储

             

    取出 int类型a 的 地址 第一位方法:

    *(char*)&a

    int* 强制转换为 char*,再解引用,即可取出一位地址的内容

                 

    实现代码:

    1. #include
    2. int check_sys()
    3. {
    4. int a = 1;
    5. //要大于一个字节的数据,才有顺序可言
    6. 判断大小端
    7. //if (*(char*)&a == 1)
    8. // //把int*强制转换a的地址为char*再解引用,判断地址第一位的内容
    9. //{
    10. // return 1;
    11. //}
    12. //else
    13. //{
    14. // return 0;
    15. //}
    16. //可以直接写成
    17. return *(char*)&a;
    18. }
    19. int main()
    20. {
    21. int ret = check_sys();
    22. if (ret == 1)
    23. {
    24. printf("小端\n");
    25. }
    26. else
    27. {
    28. printf("大端\n");
    29. }
    30. return 0;
    31. }

                

                      

    (5). 练习:(重点在注释)

                 

    1. 区别 unsigned 和 signed :

    1. //练习:1.区别 unsigned 和 signed
    2. #include
    3. int main()
    4. {
    5. char a = -1; //char 在 VS中 默认是 signed char
    6. //10000000000000000000000000000001 -- 原码
    7. //11111111111111111111111111111110 -- 反码
    8. //11111111111111111111111111111111 -- 补码
    9. // -1是整数,存进char类型中会发生截断
    10. // 11111111 -- 补码,截断获取最低8位
    11. // 最高位 是 符号位
    12. signed char b = -1;
    13. //和 char a 相同
    14. unsigned char c = -1;
    15. // 11111111 -- 补码,截断获取最低8位
    16. // 最高位 是 数值位
    17. printf("a=%d,b=%d,c=%d", a, b, c);
    18. //%d - 十进制形式打印有符号整型数据,
    19. //这里会发生 整型提升
    20. // 11111111 -- 补码,截断获取最低8位
    21. //
    22. // 整型提升,有符号位按符号位补满,补满后:
    23. //11111111111111111111111111111111 -- 整型提升后补码
    24. //11111111111111111111111111111110 -- 反码
    25. //10000000000000000000000000000001 -- 原码
    26. //
    27. // 整型提升,无符号位高位补0,补满后:
    28. //00000000000000000000000011111111 -- 整型提升后补码
    29. // 整数原码、反码、补码相同
    30. return 0;
    31. }

                    

                

    2. 使用 %u 打印 有符号整型:

                

    (补充:%u -- 十进制形式打印无符号整型

                   

    (1). 打印 -128

    1. //练习:2.使用 %u 打印 有符号整型:
    2. #include
    3. int main()
    4. {
    5. char a = -128;
    6. //10000000000000000000000010000000 -- 原码
    7. //11111111111111111111111101111111 -- 反码
    8. //11111111111111111111111110000000 -- 补码
    9. // 截断后:
    10. // 10000000 -- 截断后补码
    11. printf("%u\n", a);
    12. // %u -- 十进制形式打印无符号的整型
    13. // 对 char 变量 打印 整型数字,进行整型提升
    14. // char类型 有符号位,按符号位补满:
    15. //11111111111111111111111110000000 -- 补满后的补码
    16. //因为是以 无符号整数 打印,所以 原码、反码、补码 相同
    17. // 那么这个数就很大了
    18. return 0;
    19. }

                           

    (2). 打印 128

    1. //练习:3.使用 %u 打印 有符号整型:
    2. #include
    3. int main()
    4. {
    5. // -128 改成 128
    6. char a = 128;
    7. //00000000000000000000000010000000 -- 原码
    8. //11111111111111111111111101111111 -- 反码
    9. //11111111111111111111111110000000 -- 补码
    10. // 截断后:
    11. // 10000000 -- 截断后补码
    12. // 跟-128是一样的,
    13. //只是原码的符号位不一样,但截断后都是10000000
    14. printf("%u\n", a);
    15. // %u -- 十进制形式打印无符号的整型
    16. // 对 char 变量 打印 整型数字,进行整型提升
    17. // char类型 有符号位,按符号位补满:
    18. //11111111111111111111111110000000 -- 补满后的补码
    19. //因为是以 无符号整数 打印,所以 原码、反码、补码 相同
    20. // 那么这个数就很大了
    21. return 0;
    22. }

                    

                   

    3. 用%d打印:有符号整型 + 无符号整型

    1. //练习:3. 用%d打印:有符号整型 + 无符号整型
    2. #include
    3. int main()
    4. {
    5. //有符号整型:
    6. int i = -20;
    7. //10000000000000000000000000010100 -- 原码
    8. //11111111111111111111111111101011 -- 反码
    9. //11111111111111111111111111101100 -- 补码
    10. //无符号整型:
    11. unsigned int j = 10;
    12. //00000000000000000000000000001010 -- 原码
    13. // 原码、反码、补码 相同
    14. printf("%d\n", i + j);
    15. // i的补码 和 j的补码 相加
    16. //11111111111111111111111111101100 -- i的补码
    17. // +
    18. //00000000000000000000000000001010 -- j的补码
    19. // =
    20. //11111111111111111111111111110110 -- 两补码相加后的补码
    21. //该补码再通过%d打印有符号数,最高位是符号位,知道补码,要计算得到原码
    22. //11111111111111111111111111110101 -- 反码
    23. // 反码 符号位 不变,其它位 按位取反
    24. //10000000000000000000000000001010 -- 原码 -》 -10
    25. return 0;
    26. }

                    

                   

    4. 使用无符号整数(无负数)进行自减循环

    1. //使用无符号整数(无负数)进行自减循环
    2. #include
    3. #include
    4. int main()
    5. {
    6. unsigned int i;
    7. for (i = 9; i >= 0; i--)
    8. {
    9. printf("%u\n", i);
    10. Sleep(1000);
    11. //单位是毫秒,休眠1秒再继续下个语句
    12. }
    13. return 0;
    14. }

                   

                      

    5. 字符数组存储整型数字:

    1. //字符数组存储整型数字:
    2. #include
    3. #include
    4. int main()
    5. {
    6. char a[1000];
    7. //char类型数组,整型数字只能存储0~-128
    8. int i;
    9. for (i = 0; i < 1000; i++)
    10. {
    11. a[i] = -1 - i;//-1,-2......
    12. }
    13. //-1,-2,-3...-128,127,126,...3,2,1,0 -- 存一轮:256个元素
    14. //-1,-2,-3...-128,127,126,...3,2,1,0 -- 存一轮:256个元素
    15. //。。。。。
    16. printf("%d", strlen(a));
    17. //strlen 是求字符串长度的,
    18. //统计的是 \0 之前出现的字符的个数
    19. // \0 的ASCII码值是 0,找到0就停止计算
    20. return 0;
    21. }

                  

                   

    6. unsigned char 取值范围:

    1. //unsigned char 取值范围:
    2. #include
    3. unsigned char i = 0;//全局变量
    4. //unsigned char 的整型取值范围是:0~255
    5. int main()
    6. {
    7. // 255 再 +1 又变成 0
    8. for (i = 0; i <= 255; i++)
    9. {
    10. printf("hello world\n");
    11. }
    12. return 0;
    13. }

  • 相关阅读:
    Redis 6.0学习指南
    TensorFlow搭建LSTM实现多变量输入多变量输出时间序列预测(多任务学习)
    Oracle12.2.0.1 RAC on vSAN with RHEL7.2安装配置手册
    《算法导论》16.2 贪心算法原理
    (1)多线程-线程的创建
    Linux 环境变量
    我在高职教STM32——LCD液晶显示(3)
    一文学会Spring,Spring最简单的入门教程(万字好文)
    设计模式简介
    tomcat端口冲突怎么办?强制关闭端口的进程!
  • 原文地址:https://blog.csdn.net/weixin_63176266/article/details/130896245