C语言进阶——数据的存储

Posted wx60f68848f3833

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言进阶——数据的存储相关的知识,希望对你有一定的参考价值。

数据的存储

深度剖析数据在内存中的存储

  1. 数据类型详细介绍
  2. 整型在内存中的存储:原码、反码、补码
  3. 大小端字节序介绍及判断
  4. 浮点型在内存中的存储解析

数据类型详细介绍

  1. C语言类型

  2. 类型的意义

  3. 类型的基本归类

数据类型在内存中的存储

一个变量的创建是要在内存中开辟空间的,空间的大小根据变量的数据类型决定。

原码、反码、补码

int main()
{
    int a=20;
    //二进制
    //0000 0000 0000 0000 0000 0000 0001 0100 原码
    //0000 0000 0000 0000 0000 0000 0001 0100 反码
    //0000 0000 0000 0000 0000 0000 0001 0100 补码
    //十六进制
    //0x00 00 00 14

    int b=-10;
    //1000 0000 0000 0000 0000 0000 0000 1010 原码
    //1111 1111 1111 1111 1111 1111 1111 0101 反码
    //1111 1111 1111 1111 1111 1111 1111 0110 补码
    //补码的十六进制
    //0xFF FF FF F6

    //整数在内存中是倒着存的
    //例如:
    //a的存储:0x14 00 00 00 
    //b的存储:0xF6 FF FF FF

    return 0 ;
}

梳理

在实际代码运行中,数据并不是按照我们写的数值顺序存储。为什么呢?

大小端介绍

1.什么是大端小端

2.为什么有大端和小端:

百度2015年系统工程师笔试题

//小程序
int check_sys(void)
{
    int a=1;
    char* p=(char*)&a;
    if(*p==1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

//优化1
int check_sys(void)
{
    int a=1;
    char* p=(char*)&a;
    //返回1,小端
    //返回0,大端
    return *p;
}

//优化2
int check_sys(void)
{
    int a=1;
    return *(char*)&a;
}

//指针类型的意义:
//1.指针类型决定了指针解引用操作符能访问几个字节:char*p;*p访问了一个字节,int*p;*p访问4个字节
//2.指针类型决定了指针+1,-1,加的或减的是几个字节;char*p;p+1,跳过一个字节,int*p;p+1,跳过4个字节

int main()
{
    //写一段代码告诉我们当前机器的字节序
    //返回1,小端
    //返回0,大端
    int ret = check_sys();
    if(ret==1)
    {
        printf("小端\\n");
    }
    else
    {
        printf("大端\\n");
    }

    return 0 ;
}

练习1

//练习1
//输出什么?
#include <stdio.h>
int main()
{
    char a=-1;
    //1000 0000 0000 0000 0000 0000 0000 0001
    //1111 1111 1111 1111 1111 1111 1111 1110
    //1111 1111 1111 1111 1111 1111 1111 1111
    //1111 1111 char类型只能存储1个字节-8个bit

    signed char b=-1;
    //1000 0000 0000 0000 0000 0000 0000 0001
    //1111 1111 1111 1111 1111 1111 1111 1110
    //1111 1111 1111 1111 1111 1111 1111 1111
    //11 11 11 11

    unsigned char c=-1;
    //0000 0000 0000 0000 0000 0000 1111 1111
    //255

    printf("a=%d,b=%d,c=%d",a,b,c);

    return 0 ;
}

//输出:a=-1,b=-1,c=255

练习2

//练习2
//输出什么
int main()
{
    char a=-128;
    //1000 0000 0000 0000 0000 0000 1000 0000 原码
    //1111 1111 1111 1111 1111 1111 0111 1111 反码
    //1111 1111 1111 1111 1111 1111 1000 0000 补码
    //1000 0000
    //1111 1111 1111 1111 1111 1111 1111 1111 整型提升(整型提升按照符号位提升)

    printf("%u\\n",a);

    return 0;
}

有符号的char的范围是:-128~127

无符号的char的范围是:0~255

练习3

//按照补码的形式进行运算,最后格式化成为有符号整数
int main()
{
    int i=-20;
    //1000 0000 0000 0000 0000 0000 0001 0100 原码
    //1111 1111 1111 1111 1111 1111 1110 1011 反码
    //1111 1111 1111 1111 1111 1111 1110 1100 补码

    unsigned int j=10;
    //0000 0000 0000 0000 0000 0000 0000 1010 补码

    //补码相加
    //1111 1111 1111 1111 1111 1111 1110 1100 补码
    //0000 0000 0000 0000 0000 0000 0000 1010 补码 
    //1111 1111 1111 1111 1111 1111 1111 0110 结果
    //1111 1111 1111 1111 1111 1111 1111 0101 结果的反码
    //1000 0000 0000 0000 0000 0000 0000 1010 结果的原码
    //-10 结果的十进数

    printf("%d\\n",i+j); //-10

    return 0 ;
}

//输出:-10

练习4

int main()
{
    unsigned int i;
    //当i为无符号数时,减到0的时候再往下减的数还是整数,会把符号位的负数标志1加进去,始终是正数
    //可是当i减到负数,是一个很大的数时,i!=9,怎么进入循环?
    for(i=9;i>=0;i++)
    {
        printf("%u\\n",i);
    }

    return 0 ;
}

//结果:无限循环

练习5

int main()
{
    char a[1000];//char的范围:-128~127
    int i;
    for(i=0;i<1000;i++)
    {
        a[i]=-1-i;//-1~128 127~0
    }
    printf("%d",strlen(a));//255

    return 0 ;
}

//输出:255

练习6

unsigned char i=0;
int main()
{
    for(i=0;i<=255;i++)//255+1=0
    {
        printf("hello world\\n");
    }

    return 0 ;
}

//结果:无限打印hello world

浮点型在内存中的存储解析

常见的浮点数:

浮点数存储的例子:

int main()
{
    int n=9;
    float *pFloat=(float *)&n;
    printf("n的值为:%d\\n",n);//9
    printf("*pFloat的值为:%d\\n",*pFloat);//0.000000

    *pFloat=9.0;
    printf("num的值为:%d\\n",n);//1091567616
    printf("*pFloat的值为:%f\\n",*pFloat);//9.000000  

    return 0 ;
}

//输出:
//9
//0.000000
//1091567616
//9.000000

//总结:整型和浮点数存储不同

浮点数的存储的详细解读

根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:

IEEE 754规定:

  • 对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M
  • 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位位有效数字M

IEEE 754对有效数字M和指数E,还有一些特别规定。前面说1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

至于指数E,情况就比较复杂。

首先,E为一个无符号整数( unsigned int )这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数127/1023,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

int main()
{
    float f = 5.5;
    //5.5
    //101.1
    //(-1)^0 * 1.011 * 2^2
    //S = 0
    //M = 1.011
    //E = 2 (E=2+127=129)
    //S(1bit)  E(8bit)  M(23bit)  
    //0        10000001 011 0000 0000 0000 0000 0000
    //0100 0000 1011 0000 0000 0000 0000 0000
    //4 0 B 0 0 0 0 0

    return 0;
}

然后,指数E从内存中取出还可以再分成三种情况∶

E不全为0或不全为1

E全为0

E全为1

int main()
{
    int n=9;
    //0000 0000 0000 0000 0000 0000 0000 1001 原码/补码

    float *pFloat=(float *)&n;
    printf("n的值为:%d\\n",n);//9

    printf("*pFloat的值为:%f\\n",*pFloat);//0.000000
    //0000 0000 0000 0000 0000 0000 0000 1001
    //0.000 0000 0000 0000 0000 1001
    //(-1)^0 * 0.000 0000 0000 0000 0000 1001 * 2^-126
    //结果无限接近于0,且只打印小数点后6位

    *pFloat=9.0;
    //1001.0
    //1.001 * 2^3
    //(-10)^0 * 1.001 *2^3
    //0 10000010 001 0000 0000 0000 0000 0000 
    //

    printf("num的值为:%d\\n",n);//1091567616
    //1001.0
    //1.001 * 2^3
    //(-10)^0 * 1.001 *2^3
    //0 10000010 001 0000 0000 0000 0000 0000 
    //1091567616

    printf("*pFloat的值为:%f\\n",*pFloat);//9.000000  

    return 0 ;
}

以上是关于C语言进阶——数据的存储的主要内容,如果未能解决你的问题,请参考以下文章

C语言进阶——数据在内存中的存储

C语言进阶——数据在内存中的存储

我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段

我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段

C语言进阶笔记用图文+代码让你深度理解数据在内存中的存储

超详细的c进阶教程!C语言数据存储剖析