完全自学C(干货) —— 数据的存储

目录

一,类型的基本归类

二,大小端

三,整型在内存中的存储

四,浮点类型在内存中的存储


  • 数据已知类型有
    • 内置类型(char,int,float,double等);
    • 构造类型(数组、结构体、枚举、联合);
    • 指针类型;
    • 空类型(void);
  • 数据类型的作用,开辟访问内存大小;

一,类型的基本归类

  • 整型家族(unsigned、signed)
  • 浮点家族(float、double)
  • 构造类型(arry、struct、enum、union)
  • 指针类型
  • 空类型(void)
//整型家族
//char
	unsigned char 
	signed 

//short [int]表示可省略
	unsigned short[int] 
	signed short[int] 

//int
	unsigned int
	signed int

//long
	unsigned long[int] 
	signed long[int] 
//浮点家族
    float
    double
//构造类型
    数组类型
    结构体类型 struct
    枚举类型 enum
    联合类型 union
//指针类型
    char* p
    int* p
    float* p 
    void* p
    ...等
//空类型
//通常作为函数的返回类型、函数的参数、指针类型
    void 表示无类型
    void test(void)

二,大小端

大小大于一个字节时,就会存在多个字节安排排序的问题;

  • 大小端,即数据内存存储的地址顺序;
  • 大端(大端字节序),值数据的低位保存在内存的高地址中,而数据的高位保存内存的低地址;
  • 小端(小端字节序),值数据的低位保存在内存的低地址中,而数据的高位保存内存的高地址;
int checkSystem()
{
	int num = 1;
	//小端内存存储顺序应为 01 00 00 00
	//大端内存存储顺序应为 00 00 00 01
	char* p = # //解引用时会仅访问1个字节
	return *p;
}
int main()
{
	if (checkSystem())
		printf("小端");
	else
		printf("大端");
}
//结果:小端
//visual studio 2019为小端编译器

三,整型在内存中的存储

  • 创建变量,即会在内存中开辟空间;
  • 空间大小,由数据类型决定;

有符号整数(signed)

  • 符号位(0/1) + 数值位组成;
  • 数据在内存中存放的是二进制补码;
  • 正整数,原码、反码、补码相同;

有三种二进制形式(原码、反码、补码):

  • 原码,直接按照正负数直接翻译成的二进制;
  • 反码,在原码基础上符号位不变,其余取反;
  • 补码,原码转化为反码 + 1;
int main()
{
	//正整数,原码、反码、补码相同
	int a = 10;
	//0000 0000 0000 0000 0000 0000 0000 1010 内存补码形式
	//0a 00 00 00 小端十六进制形式

	//负整数
	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 补码,反码+1
	//f6 ff ff ff 小端十六进制形式
}


使用原反补码原因:

  • 计算机中,整数数值一律用补码来表示和存储;
  • 使用补码可将符号位和数值域统一处理(即运算);
  • 使得加减法也可统一处理(CPU只有加法器);
  • 此为补码和原码相互转换其运算过程是相同的,无需额外的硬件电路;
    • 可将补码取反+1得到原码;

注:先翻译为补码运算,再还原为原码输出;

补充:

  • char,是signed还是unsigned取决于编译器;
  • int,即是signed int;
  • short,即是signed short;

注:头文件limts.h,可查看取值范围;

//16进制书写,即补码
int a = 0xffffffff; //-1
unsigned int b = 0xffffffff; //4294967295

字符char类型

  • 有符号char类型,取值范围为 -128 ~ 127(有符号1000 0000直接翻译为-128);
  • 无符号char类型,取值范围为 0 ~ 255;
  • char类型超过取值范围,会扣除256,循环取值,即正向越界-256,负向越界+256;

int main()
{
	//有符号char类型,-128~127
	char a = 200; //实际存储-56
	char b = -200; //实际存储56

	//无符号char类型,0-255
	unsigned char c = 200; //实际存储200
	unsigned char d = -200; //实际存储56

	printf("%d %d %d %d\n", a, b, c, d);
}
int main()
{
	//逻辑顺序为,先将值存入变量内,考虑是否截断
	//在将变量的补码元素,此时需考虑整型提升问题
	//计算的结果补码,看是否需要转换为原码

	char a = 128; //其实a实际值为-128,因为值已越界
	char b = 5;
	printf("%d %u", a + b, a + b); 
	//1000 0000 a补码
	//0000 0101 b补码 
	
	//a+b,先需整型提升
	//1111 1111 1111 1111 1111 1111 1000 0000 a提升后补码
	//0000 0000 0000 0000 0000 0000 0000 0101 b提升后补码
	//1111 1111 1111 1111 1111 1111 1000 0101 a+b后补码 

	//%d为有符号整数,需按规则转换为原码
	//1111 1111 1111 1111 1111 1111 1000 0100 反码
	//1000 0000 0000 0000 0000 0000 0111 1011 原码,-123

	//%u表示无符号整数,原反补相同,无需转换
	//1111 1111 1111 1111 1111 1111 1000 0101 补码,4,294,967,173
}
//结果:-123 4294967173

注:运算时涉及整形提升或算术转换

四,浮点类型在内存中的存储

头文件float.h,可查看取值范围;

浮点类型的数据存储,使用另外一种方式,与整型数据存储完全不一样;

int main()
{
	int a = 10;
	//0a 00 00 00 a内存十六进制补码形式

	float b = 10;
	//00 00 20 41 b内存十六进制补码形式

	printf("%d %f", a, b); //结果:10 10.000000
}
//同样值,内存存储的形式完全不一样

浮点型内存存储规则

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

  • (-1)^S * M * 2^E
  • (-1)^S,表示符号位0、1;
  • M,表示有效数字,大于等于1,小于2;
  • 2^E,表示指数位,即使用科学计数法移位的值;
float a = 10;
//十进制10的二进制为1010,按照规则(-1)^0 * 1.010 * 2^3
//S = 0
//M = 1.010
//E = 3

float b = -5;
//十进制-5的二进制为101,按照规则(-1)^1 * 1.01 * 2^2
//S = 1
//M = 1.01
//E = 2

float c = -0.5;
//十进制-0.5的二进制可类似为-0.101,按照规则(-1)^1 * 1.01 * 2^-1
//S = 1
//M = 1.01
//E = -1

S,M,E内存中的分布

  • IEEE754规定,32位浮点数,S(1位),E(8位),M(23位)
  • IEEE754规定,64位浮点数,S(1位),E(11位),M(52位);

(1<=M<2),有特别规定:

  • 规定需写为1.******,但默认首位1省去,只写小数点后的部分;
  • 读取的时候在添加上1,这样可保存24位有效数字(32位存储);

E (unsigned int,为无符号数)

  • 在32位存储为8位,则取值范围为0-255;
  • 在64位存储为11位,则取值范围为0-2047;
  • 因为E可能会取到负数,所以实际上规定要加个中间数,即+127或+1023;
//32位存储
float a = 10;
//十进制10的二进制为1010,按照规则(-1)^0 * 1.010 * 2^3
//S = 0
//M = 1.010 -> 0100 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = 3     -> 1000 0010 (加127,130)

//0100 0001 0010 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//41 20 00 00 
//00 00 20 41 小端内存十六进制形式

float b = -5;
//十进制5的二进制为101,按照规则(-1)^1 * 1.01 * 2^2
//S = 1
//M = 1.01 -> 0100 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = 2    -> 1000 0001 (加127,129)

//1100 0000 1010 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//c0 a0 00 00 
//00 00 a0 c0 小端内存十六进制形式

float c = -0.5;
//十进制0.5的二进制可类似为0.1(二进制11相对于1*2^1+1*2^0=3, 0.1相对于1*2^-1=0.5)
//按照规则(-1)^1 * 1.0 * 2^-1
//S = 1
//M = 1.0 -> 0000 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = -1   -> 0111 1110 (加127,126)

//1011 1111 0000 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//bf 00 00 00 
//00 00 00 bf 小端内存十六进制形式

float d = 0.75;
//十进制0.75的二进制可类似为0.11(二进制11相对于1*2^1+1*2^0=3, 0.11相对于1*2^-1+1*2^-2=0.75)
//按照规则(-1)^0 * 1.1 * 2^-1
//S = 1
//M = 1.1 -> 1000 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = -1   -> 0111 1110 (加127,126)

//0011 1111 0100 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//3f 40 00 00 
//00 00 40 3f 小端内存十六进制形式

浮点型内存还原规则

注:32位为例,二进制内存形式,还原为十进制数值时

  • 首位S符号位
  • 其后8位,-127为E
  • 最后23位,补1为M

E还原时有三种情况

  • 一般情况下,E不全为0或1:
    • E-127(或E-1023);
    • M要在首位添加1;
  • E全为0时,即+127后仍为0(原2^-127):
    • E=1-127(或1-1023),不在是0-127(或0-1023);
    • 且M的有效数字不再加1;
    • +/- 0.******的小数,这样做是为了表示±0,其实就是接近于0的很小的数字;
  • E全为1时,即+127后为255(原2^128):
    • E=255-127=128;
    • +/- 1.******,如有效数字M全为0,表示±无穷大(正负取决于符号位s);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值