各类数据在内存中的存储与读取(重点为整型和浮点型)

学习目标:

剖析数据在内存中的存储,本章重点:

  1. 数据类型详细介绍
  2. 整形数据在内存中的存储
  3. 大小端字节序介绍及判断
  4. 浮点型数据在内存中的存储

学习内容:

①:

整形家族

char(本质为ASCII的存储)、short、int、long、

long long;均有signed和unsigned.编译器默认为signed

浮点型家族 float、double

②:何为有符号、无符号?

举个栗子:vs2019为例

char:默认为signed char,当有char a=1;先写32位原码,由于正数原、反、补码一致,所以int型的1的低位(8位)bit位内的数据被截断,然后存至char所申请的空间。此时的8为bit位的最高位为符号位。这对char、short类的缺省整型精度的数据类型来说,做表达式运算时,发生整型提升至关重要!上代码:

#include <stdio.h>
int main()
{
	unsigned char c1 = 255;
	printf("%d\n", c1);//作%d打印,char、short类型数据亦作整型提升
	//8为bit存满1时,刚好为255:11 11 11 11
	//由于是unsigned char,上述最高位为有效位,所以做整型提升时:
	//按正数提升:00000000 00000000 00000000 11111111
	//对上述补码(等价于原码)打印,直接翻译即可:255
	char c2 = 255;
	//上述8位bit位的最高位为符号位,故作负数的整型提升:
	//11111111 11111111 11111111 11111111(补码)
	//翻译为原码(打印):10000000 00000000 00000000 00000001
	//上述结果为-1;
	printf("%d\n", c2);
	return 0;
}

对signed char和unsigned char类型内存空间所能存储的十进制数作总结:

(a):unsigned char:8位有效位:最大数为:2^8-1=255(因为有个0,所以减一);

(b):signed char:最高位为0或1,为0为正数,为1为负数,两者在256(绝对值)各占一半,即负数范围为:-1☞-128;而正数由于有个0,所以其范围为:0☞127.

综合上述可知:signed char 所能表示的范围为:-128☞127(-128为:10000000☞补码)

将上述推演规律拓展至short,类似,直接给出其范围:-32768☞32767(signed)

②:除了上述的整型、浮点型,这里还有一个构造类型(自定义)

数组类型(随着元素类型、元素个数变化)
结构体类型
枚举类型
联合类型

③:指针类型

意义在于:解引用及访问字节数会受其影响。

④:整型在内存中的存储:

统一形式为补码(意义:可将符号位和数值域统一处理,其与原码相互转换,运算过程相同,无需额外电路)

⑤:字节序

顾名思义:以字节为单位来排序。

补码的低位放在低地址处:小端字节序

补码的高位放在低地址处:大端字节序

例题:设计一个程序判断当前机器的字节序:(利用指针总是指向“小字节”的特点):

#include <stdio.h>
int main()
{
	int a = 1;
	char* p = (char*)&a;//&a为int*,强转成char*再赋给p
	if (*p == 1)
	{
		printf("小端\n");
	}
	else
		printf("大端\n");
	return 0;
}

note:%d和%u打印时,都是针对发生整型提升后的最高位而言的。

例题1:(打印结果时什么?)

#include <stdio.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

原因i>=0;i打印9-0没问题,再--变成-1时:补码全为1(32位),存进unsigned int申请的空间,此时的二进制位是没有符号位的,翻译成原码(无符号位时原反补全等):2^32-1=4294967295,这就导致在for循环的判断部分i>=0进行时, 为真,继续打印!

note:自己动手计算内存中的数是多少时遵循一个原则:内存中的都是补码,显示在屏幕上的都是原码!怎么把补码翻译成原码,关键就看补码的数据类型是有无符号位了。

例题2:(打印结果时什么?)

#include <stdio.h>
#include <string.h>
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d",strlen(a));
	return 0;
}

分析:a[i]依次为:-1、-2、...、-128(因为数组元素类型为char,负数的上限为-128,此时的补码为:10 00 00 00);再减一:01 11 11 11翻译成原码:2^7-1=127.之后再继续减减的操作直至0.

strlen从a进入,将元素当作unsigned char来翻译,只要不是‘\0’(ASCII为0(补码)),统统每进一位就加一,所以说:计数至0.strlen功能完成。那此时的字符个数是:128+127=255个(0不算一个)。

⑥:浮点型数据在内存中的存储

准则:IEEE754:任意一个二进制浮点数V可以表示成以下形式:

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

s:表正负:0为正,1为负

M:有效数字,范围:1<=M<2

E:指数位(10^3在十进制中的指数位就是3,二进制就是2^指数位)

5.5写成二进制:101.1再科学计数法:1.011*2^2.再写成上述标准格式:(-1)^0*1.011*2^2

那s、m、e分别为:0、1.011、2

难以存储的二进制小数(浮点数):3.3

因为小数点后一位为:2^-1

小数点后两位为:2^-2

...

存储:

float:(4字节,32位bit):从低地址至高地址依次存S、E、M,各自分配的bit的数量为:1、8、23

double:(8字节,64位bit):亦依次存S、E、M,各自分配的bit数量为:1、11、52

对S/E/M存储规则的介绍:

S:0/1存进低地址首位bit中,无需多说

M:由于其范围为1<=M<2;即必为1.几,敢情1直接不存了,只存小数点后面的.几的“几”。等到读取时再把这个省略的1补上。这么做的目的是:多一位有效位!还原(读取)时记得加一。

E:首先它是一个unsigned int :有效位则为8位,范围则来到:0☞255,若是double,更是来到:0☞2047,由于科学计数法是允许E为负数的。对此,unsigned int与负数存在矛盾。IEEE754则对此作出规定:存入内存的E值须再加上0☞255或者0☞2047的一个中间数,即127和1023.再存进属于E的那块空间。如2^10,E是10,存的话,float:存的是10+127=137的补码,double存的是:10+1023=1033的补码。

举例:存float的0.5:

S:0

E:-1+127=126:补码(等价于原码):01 11 11 10

M:1.0:1不要就存23个0:00000000 00000000 0000000
总的:

0  01111110  00000000000000000000000

对存于内存中的二进制浮点数的读取:

分情况:

(a):E不全为0或不全为1(最一般的情况)

   由上述:E的真实值则为内存中的补码翻译成原码后再减那个中间数(float还是double?),M再加个1。

(b):E全为0:上述可知E是加过中间数作存储的,加完还全是0,你说刚开始的E是有多小!?,所以说此时的浮点数,几乎接近于0;此时的M不必再加1还原了,直接打印吧。

(c):E全为1:这时:如果有效数字全为0表示±无穷大。

对浮点数存储理解的一个经典案例:

#include <stdio.h>
int main()
{
	int n = 9;
	//9的补码:00000000 00000000 00000000 00001001
	float* pfloat = (float*)&n;
	printf("n的值为:%d\n", n);//为9不难理解
	printf("pfloat的值为:%f\n", *pfloat);
	//以%f打印9的补码,此时的补码被视作浮点型数据的存储方式:
	//0 00000000 00000000000000000001001
	//此时属于E全为0的情况,E真实值为0-127=-127,M为0.00000000000000000001001
	//还原出的数字非常接近于0;而%f只能精确到小数点后6位,故只可打印0.000000
	*pfloat = 9.0;
	//9.0写成二进制数:(-1)^0*1.001*2^3
	//存:0 10000010 00100000000000000000000
	printf("num的值为:%d\n", n);
	//以%d的形式去解读上述补码,为一个正数,直接当做原码打印:1091567616

	printf("*pfloat的值为:%f\n", *pfloat);
	//9.000000
	return 0;
}

运行结果也确实是:

 


学习时间:

2021.10.7


学习产出:


1、 技术笔记 2 遍
2、CSDN 技术博客 31篇
3、 gitee

Guess you like

Origin blog.csdn.net/weixin_55667484/article/details/120640200