• 【C#数据的存储】


    目录

    一、数据类型基本归类

    二、整数的存储

            原码反码补码

            大小端字节序

            练习一

            练习二

    三、浮点数的存储


    一、数据类型基本归类

    signed:有符号

    unsigned:无符号

    数据在内存中默认为signed,除了char,char是否有符号取决于不同编译器。

    整型:

    char(char类型的本质是ASCII码值)、short、int、long

    浮点型:

    float、double

    构造类型:

            数组类型:int [n]、char [n] ......

            n表示元素个数,元素个数不同,类型就不同,如int [1] 和 int [2] 就是不同类型

            结构体类型:struct

            枚举类型:menu

            联合类型:union

    指针类型:

    int* p、char* p、float* p、void* p ......

    空类型:void

    通常用于函数返回类型,函数参数,指针类型。

    ☆C语言中没有字符串类型,只能靠字符数组存储

    二、整数的存储

            原码反码补码

    整数在内存中以补码的方式存储。

    原码:整数转化为二进制得到,最高位为符号位(1为负数,0为正数)

    反码:原码符号位不变,其他位按位取反得到

    补码:反码+1得到

    正数的原码反码补码相同。

    负数需要通过转换。

    为什么在内存中整数要以补码的方式存储?

    这是因为:

    1.使用补码,可以将符号位和数值域统一处理,同时,加法和减法也可以统一处理(将减法转化为加法)。

    2.补码与原码原码相互转换,其运算过程相同,无需额外硬件电路。

    简单来说就是:

    原码(符号位不变按位取反)→反码(+1)→补码

    补码(符号位不变按位取反)→反码(+1)→原码

     我们可以通过查看变量中存放的数据来得知整数在内存中是否存放的是补码。

     由此可见整数在内存中存放的是补码,但是顺序是反着的,这是因为大小端字节序的问题。

            大小端字节序

    为什么有大小端字节序?

    计算机内部优先处理低字节,效率更高,所以在大部分计算机中都是使用小端存储模式,而在其他一些场景中,大端存储模式效率更高。

    大端:将数值低位放入高地址处,高位放入低地址处。

    小端:将数值低位放入低地址处,高位放入高地址处。

    在我的编译器下,存储方式为小端。

            练习一

     写一段判断当前机器大小端字节序的代码。

    1. #include<stdio.h>
    2. int main()
    3. {
    4. int a = 1;
    5. if (*(char*)&a)//将a的地址强制转换为char*类型,一次只能向后访问1个字节
    6. printf("小端");
    7. else
    8. printf("大端");
    9. return 0;
    10. }

            练习二

    判断下列代码输出结果

     1.

    1. int main()
    2. {
    3. char a = -1;
    4. signed char b = -1;
    5. unsigned char c = -1;
    6. printf("a=%d,=%d,c=%d", a, b, c);
    7. return 0;
    8. }

    答案:-1,-1,255

    a 和 b 都是正常打印,主要分析 c

    2. 

    1. int main()
    2. {
    3. char a = -128;
    4. printf("%u\n", a);
    5. return 0;
    6. }

    答案:4294967168

     3.

    1. int main()
    2. {
    3. char a = 128;
    4. printf("%u\n", a);
    5. return 0;
    6. }

    答案:4294967168

     4.

    1. int main()
    2. {
    3. int i = -20;
    4. unsigned int j = 10;
    5. printf("%d\n", i + j);
    6. return 0;
    7. }

    答案:-10

    5.

    1. int main()
    2. {
    3. unsigned int i;
    4. for (i = 9; i >= 0; i--)
    5. {
    6. printf("%u\n", i);
    7. }
    8. return 0;
    9. }

    答案:死循环。

     

    1. int main()
    2. {
    3. char a[1000];
    4. int i;
    5. for (i = 0; i < 1000; i++)
    6. {
    7. a[i] = -1 - i;
    8. }
    9. printf("%d", strlen(a));
    10. return 0;
    11. }

    答案:255

    首先我们要知道char类型的取值范围是:-128~127

    可以将它们想象成一个圈。

    为什么10000000是-128

     同时,我们也可以观察到,在这个‘蛋’中,只要不断加1,就可以得到下一个数,反之,不断-1,也可以得到下一个数。

    只有当-1+1时,会得到0,而strlen遇到0会停下,因为 i 是从0开始的,所以我们只需要求出从0到1之间有多少个数字就可以了,这时我们根据图片就可以看出从0~1正好是一整个循环,其中共有255个数字。

    6.

    1. unsigned char i = 0;
    2. int main()
    3. {
    4. for (i = 0; i <= 255; i++)
    5. {
    6. printf("hello world\n");
    7. }
    8. return 0;
    9. }

    答案:死循环

    无符号char类型的取值为0~255,当值为255时再+1,就又变回1,再次循环。

    7.

    1. int main()
    2. {
    3. unsigned int a = -1;
    4. printf("%d", a);
    5. return 0;
    6. }

    答案:-1

    打印结果是根据打印类型为准的,不管数据原来的类型。

    -1在内存中为:11111111111111111111111111111111

    虽然类型是unsigned int ,但是打印是以 int 类型打印,所以还是将根据符号位判断这个数为负数,所以需要转换为原码,最后得到-1。

    8.

    1. int main()
    2. {
    3. if (strlen("abc") - strlen("abcdef") >= 0)
    4. printf(">");
    5. else
    6. printf("<");
    7. return 0;
    8. }

    答案:>

    因为 strlen 返回类型为 size_t = unsigned int (无符号整型),而一个无符号整型减去无符号整型,最后的结果还是无符号整型,不可能是负数。

    三、浮点数的存储

     首先来一段代码,看它的输出结果。

    1. int main()
    2. {
    3. int n = 9;
    4. float* p = (float*)&n;
    5. printf("n的值为:%d\n", n);
    6. printf("*p的值为:%d\n", *p);
    7. *p = 9.0;
    8. printf("n的值为:%d\n", n);
    9. printf("p的值为:%d\n", *p);
    10. return 0;
    11. }

    输出结果:

    虽然不知道为什么会是这样的结果,但是我们可以初步得出结论:整数和浮点数在内存中的存储方式是不同的。

    浮点数的存储规则

     任意一个二进制浮点数都可以表示成以下形式:

    V=(-1)^S*M*2^E

    V表示任意一个浮点数

    S代表符号位,当S=0时,V为正数,当S=1时,V为负数

    M代表一个有效数字,大于1,小于2

    E表示指数位

    而最后在内存中存储的就是S、M、E这三个值

    简单来说,就是先将浮点数写成科学计数法的形式,最后在前面加上正负就可以了。

     不精准的情况:

    不同类型浮点数存储方式:

     对于32位的浮点数(float),最高的一位是符号位S,接着8位是指数E,剩下23位为有效字母M

     对于64位的浮点数(double),最高的一位是符号位S,接着11位是指数E,剩下52位为有效字母M

     在存储浮点数时,还有如下规则:

    1.由于M始终是以1开头,所以将M存入内存中时,省去开头的1,拿出数据时再加上。这样就可以多存一位数,提高数据精度。

    2.将E的值设为无符号类型,这样可以存放更多的数据。

    到第二条就出现问题了,因为我们的E是有可能出现负数的。

    这时,为了不出现负数,规定在E存入内存时,需要加上一个中间数,float类型下中间数为127,double类型下中间数为1023

    例如,在 float 下E为2时,存入内存中的值就是2+127=129,写成二进制就是10000001

    验证存储规则:

     在取出浮点数的时候,又分为三种情况:

    1. E不为全0或不为全1

    将指数的值减去127(或1023),得到真实值,再将有效数字M加上原来省去的1,得到原来的值,进行正常运算。

    2. E为全0

    直接将E的值等于1-127= - 126,M前也不加上1,而是变成0,为了表示0或是接近于0的数字。

    3. E为全1

    表示为正负无穷大

    解释题目:

    1. int main()
    2. {
    3. int n = 9;
    4. float* p = (float*)&n;
    5. printf("n的值为:%d\n", n);
    6. printf("*p的值为:%d\n", *p);
    7. *p = 9.0;
    8. printf("n的值为:%d\n", n);
    9. printf("p的值为:%d\n", *p);
    10. return 0;
    11. }

  • 相关阅读:
    2022/8/9
    js逆向之浏览器环境补充
    VLAN多变一的业务场景解答
    脱口秀与ST洲际的“玩笑”故事,掀开了市场隐秘的角落
    GFS分布式文件系统
    微软推送win11 22622.575补丁!
    webGoat目录访问控制路径
    Flink的API分层、架构与组件原理、并行度、任务执行计划
    Google Earth Engine(GEE)——一个简单的sentinel-2影像函数下载APP
    VBA技术资料MF83:将Word文档批量另存为PDF文件
  • 原文地址:https://blog.csdn.net/Domeecky/article/details/124993781