数据在内存的存储形式

第一:数据类型分类

1.1 基本内置类型

类型 别名 所占字节 解释
char signed char 1 字符数据
short signed short [int] 2 短整型
int signed [int] 4 整型
long signed long [int] 4 长整型
long long signed long long [int] 8 长长整型
float - 4 单精度浮点型
double - 8 双精度浮点型
long double - 8 长双精度浮点型
#include <stdio.h>
int main()
{
    printf("char        %d\n", sizeof(char));
    printf("short       %d\n", sizeof(short));
    printf("int         %d\n", sizeof(int));
    printf("long        %d\n", sizeof(long));
    printf("long long   %d\n", sizeof(long long));
    printf("float       %d\n", sizeof(float));
    printf("double      %d\n", sizeof(double));
    printf("long double %d\n", sizeof(long double));
    return 0;
}

数据在内存的存储形式

1.2 取值范围

类型 最小值 最大值 最小值 最大值
char -2^7 2^7-1 SCHAR_MIN:-128 SCHAR_MAX:127
unsigned char 0 2^8-1 0 UCHAR_MAX:0xff/255
short -2^15 2^15-1 SHRT_MIN:-32768 32767
unsigned short 0 2^16-1 0 USHRT_MAX:0xffff/65535
int -2^31 2^31-1 INT_MIN:-2147483648 INT_MAX:2147483647
unsigned int 0 2^32-1 0 UINT_MAX:0xffffffff/4294967295
long -2^31 2^31-1 LONG_MIN:-2147483648L LONG_MAX:2147483647L
unsigned long 0 2^32-1 0 ULONG_MAX:0xffffffffUL/4294967295L
long long -2^63 2^63-1 LLONG_MIN:-9223372036854775807i64 - 1 LLONG_MAX:9223372036854775807i64
unsigned long long 0 2^64-1 0 ULLONG_MAX:0xffffffffffffffffui64
#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf("char min and max                  = %d\t\t%d\n", SCHAR_MIN, SCHAR_MAX);
    printf("unsigned char min and max  = %d\t\t%d\n", 0, UCHAR_MAX);
    printf("short min and max                 = %d\t\t%d\n", SHRT_MIN, SHRT_MAX);
    printf("unsigned short min and max = %d\t\t%d\n", 0, USHRT_MAX); 
    printf("int min and max                     = %d\t\t%d\n", INT_MIN, INT_MAX);
    printf("unsigned int min and max     = %d\t\t%#x\n", 0, UINT_MAX);
    printf("long min and max                  = %ld\t\t%ld\n", LONG_MIN, LONG_MAX);
    printf("unsigned long min and max  = %d\t\t%#x\n", 0, ULONG_MAX);
    printf("long long min and max           = %lld\t\t%lld\n", LLONG_MAX, LLONG_MAX);
    printf("unsigned long long min and max  = %d\t\t%llu\n", 0, ULLONG_MAX);
    return 0;
}

数据在内存的存储形式

1.3 构造数据

类型 关键字
数组 []
结构体 struct
联合体 union
枚举 enum

1.4 指针和空类型

指针类型是指在类型后面加上一颗*,表示,好比:int* p;表示p是一个整型指针,指向的内存空间为int。
    空指针:void *,好比内存函数:malloc calloc等函数的返回值都为void*类型,须要强制将其转化为指定类型的指针,如:int arr = (int*)malloc(10 * sizeof(10)),即申请了10个int的内存空间。

第二:整型的存储方式

2.1 存储方式:

整型数据在内存中是以补码的方式进行存储的。
整型:算法

  1. 有符号位的整型
    1.1. 正数:原码=反码=补码
    1.2. 负数:反码=原码的除符号位以外,其他位取反,补码 = 反码+1
  2. 无符号位整型:原码=反码=补码

    2.2 大小端存储方式

    是指字节在内存中存放的顺序。
    分为大端存储(大端字节存储)和小端存储(小端字节存储)
Column 1 内存低地址 内存高地址
小端 数据补码的低位 数据补码的高位
大端 数据补码的高位 数据补码的低位

判断系统是以哪一种方式存放数据的。
以下面例子:
数据在内存的存储形式数组

// 小端:返回1,大端:返回0
int check_sys()
{
    int a = 1;
    char* p = (char*)&a;
    if (1 == *p)
    {
        return 1;
    }
    else
    { 
        return 0;
    }
}

// 小端:返回1,大端:返回0
int check_sys_v1()
{
    int a = 1;
    char* p = (char*)&a;
    return *p; // 由于*p == 1,返回1,*p == 0,返回0,因此直接返回*p
}

// 小端:返回1,大端:返回0
int check_sys_v2()
{
    int a = 1;
    return *(char*)&a; // 由于*p == 1,返回1,*p == 0,返回0,因此直接返回*p
}

int main()
{
    int a = 1024;
    int ret = check_sys_v2(a);
    if (0 == ret)
    {
        printf("大端字节模式\n");
    }
    else
    {
        printf("小端字节模式\n");
    }
    return 0;
}

数据在内存的存储形式

2.2.1 例1:

int main()
{
    char a = -1;  // 由于a是char类型,只占1个字节,且是小端字节存储模式,因此将-1的补码的低8位所占的字节上的值截取给char a:11111111
    signed char b = -1;   // signed char b 等价于char a
    unsigned char c = -1; // c存的补码也是11111111,可是为无符号的整型,则原码=反码=补码,故原码为11111111,即255
    // 11111111111111111111111111111111  - -1的补码
    // 由于a是char类型,只占1个字节,且是小端字节存储模式,因此将存放在低地址中的a的低数据位中的8个比特位截取给a
    // 故 a = 11111111 - 补码
    // b是signed char 能够简写为char b,故与a数据类型相同
    // 故 b = 11111111 - 补码
    // 因为c也是char类型
    // 故 c = 11111111 - 补码 
    // 由于打印时,是以整型进行打印的,因此须要作提高(按照原符号位进行提高)
    // 因为a是有符号的,且符号位1,则补1,即为:11111111111111111111111111111111,
    //    前24个1为补的1,则其原码为1000...0001,为-1
    // b和a都为有符号的char,故他们相同
    // c是无符号的char,则补0进行提高,即为00000000000000000000000011111111,其原码=反码=补码,故为255
    printf("a = %d,b = %d,c = %d\n", a, b, c);
    return 0;
}

数据在内存的存储形式

2.2.2 例2:

int main()
{
    char a = -128;
    // 10000000 00000000 00000000 10000000 - 原码
    // 11111111 11111111 11111111 01111111 - 反码
    // 11111111 11111111 11111111 10000000 - 补码
    // a 为char占一个字节:因此截取低地址所存取的值(数据的低位):10000000
    // char a:10000000
    // %u为无符号的整型,因此要作提高,因为char a为有符号,因此提高位补1:
    // 11111111 11111111 11111111 10000000 (提高后),提高后为无符号,因此其原码=反码=补码其对应的十进制为;4294967168
    printf("%u\n", a); 
    return 0;
}

数据在内存的存储形式

2.2.3 例3:

int main()
{
    char a = 128;
    // 00000000 00000000 00000000 10000000 - 原码=反码=补码

    // a 为char占一个字节:因此截取低地址所存取的值(数据的低位):10000000
    // char a:10000000
    // %u为无符号的整型,因此要作提高,因为char a为有符号,因此提高位补1:
    // 11111111 11111111 11111111 10000000 (提高后),提高后为无符号,因此其原码=反码=补码其对应的十进制为;4294967168
    printf("%u\n", a); 
    return 0;
}

数据在内存的存储形式

2.2.4 例4:

int main()
{
    //9 8 7 6 5 4 3 2 1 0 2^32-1 ...0 2 ^ 32 - 1 ...0往复进行死循环
    // 由于当i==0时打印后,执行i--;即i = 0-1 = 0 + (-1) = 32个1
    // i 为无符号整型,因此原码和补码相同,即i为2^32-1
    unsigned int i;
    for (i = 9; i >= 0; i--)
    {
        printf("%u\n", i);
    }
}

2.2.5 例5:

int main()
{
    char a[1000];
    int i;
    for (i = 0; i <= 1000; i++)
    {
        a[i] = -1 - i; // -1 -2 .. -128 127 126 ... 1 0 -1 ....
    }
    printf("%d\n", strlen(a)); // 遇到0中止
    return 0;
}

unsigned char i = 0;
int main()
{
    for (i = 0; i <= 255; i++)
    {
        printf("hello, world %d\n", i);0 .. 255 0 .. 255 ...死循环
    }
    return 0;
}

第三:浮点数在内存的存数形式

3.1 国际标准:

根据国际标准IEEE(电气和电子工程协会)754规定,能够将任何一个浮点数(二进制)表示为如下的通用形式:

3.1.1 公式:

(-1)^S * M * 2^E
注释:
        1. (-1)^S表示符号位,负数:S = 1;正数:S = 0;
        2. M:表明有效位数(科学计数法),1 <= M < 2;
        3. 2^E:表明指数位数(E),如2^4,E = 4;

S M E存放顺序和大小:

(1)对于32位的浮点数:float(占4个字节,32个比特位)S M E的存放顺序和大小为:
         低地址--->高地址:
         S:存放第一位(1位);E:存放2-9位(8位);M:存放10-32位(23位)。
       (2)对于64位的浮点数:double(占8个字节,64个比特位)S M E的存放顺序和大小为:
        低地址--->高地址:
         S:存放第一位(1位);E:存放2-12位(11位);M:存放13-64位(52位)。

3.2 浮点数转为二进制的算算法步骤:

(1)整数部分(除2):如:13:
            13 / 2 = 6 ... 1
            6   / 2 = 3 ... 0
            3   / 2 = 1 ... 1
            1  /  2 = 0 ... 1 # 当商为0时,中止运算,将一次获得的余数做为二进制的低位-->高位数据,如:13:1101 

    (2)小数部分(乘2):如0.875
            0.875 * 2 = 1.750 = 1 + 0.75 # 二进制中的左边第一个1
            0.75 * 2   = 1.50 = 1 + 0.5     # 二进制中左边第二个1
            0.5 * 2 = 1.0 = 1 + 0 # 当相乘后小数位0,则中止计算将依次获得整数位做为小数二进制的高位-->低位,如:111

    如:13.5的二进制为:00001101.1,即1101.1
    将其转化为IEEE754标准:(-1) ^ 0 * 1.1011 * 2 ^ 3
    即:S = 0;M = 1.1011;E = 3

3.3 S M E 在内存中存放的值

(1)S
            若是为正数:S在内存中存放的是0
            若是是负数:S在内存中存放的是1
(2)M
            将M的小数部分存放在内存中。
 (3)E
         E是一个无符号正数;float(8为:0-255);double(11位:0-2047);
因为E能够是负数,当浮点数小于1时,因此存入内存中的E为真实的E加上一个中间数(double:1023;float:127),即:内存中E = 二进制E + 127 or 1023.
如:float的13.875的二进制:1.1011中的E为3,则内存中为3 + 127 = 128 + 2 = 10000010(2^7 + 2 ^ 1)
    例如:
           13.875在内存中的存放为:
            二进制为:1101.111
            IEEE754:(-1) ^ 0 * 1.101111 * 2 ^ 3, S = 0;M = 1.1011;E = 3
            内 存  为:0 10000010 10111100000000000000000
                          :0100 0001 0101 1110 0000 0000 0000 0000
            十六进制:41 5e 00 00,即:0x415e0000
            内存为   : 00 00 5e 41(低地址-->高地址)

数据在内存的存储形式

3.4 去读数据

(1)当E(内存中的E)中同时存在1和0
            其值-127或者1023获得的是IEEE754中公式中的E。
            内中的M中为小数(xxxx),将其加上1,变为1.xxxx
            正数:S= 0;负数:S=1
      (2)E(内存中的E)为全0 为一个无穷小的数字
            真是的E为1-127 or 1 - 1023
            M:不在加上正数部的1. 0.xxxx
           S:同上
    (3)E(内存中的E)为全1,若是有效数字全为0,则表示无穷大数字。

3.5 例子解析

int main()
{

    // 00000000 00000000 00000000 00000110 - 6的补码
    // 当以float输出时,上述为浮点数的补码,将其进行读取为:S = 0;E = 1 - 126;M = 0.11 为一个无穷小的数
    int a = 6;
    float* p = (float*)&a;
    printf("a = %d\n", a); // 6
    printf("*p = %f\n", *p); // 0.0000000

    // 6.0的二进制:110.0 -->IEEE754: (-1)^0 * 1.10 * 2 ^ 2
    // S = 0;M = 1.10;E = 2;2 + 127 = 10000001;
    // 内存:01000000 11000000 00000000 00000000
    // 因此当以整型进行输出时:为01000000 11000000 00000000 00000000,为正数,即为原码:
    *p = 6.0;
    printf("a = %d\n", a); // 01000000110000000000000000000000,十进制:1,086,324,736
    printf("*p = %f\n", *p); // 6.0
    return 0;
}

数据在内存的存储形式