数据在内存中的存储

数据在内存中的存储

一.整数在内存中的存储

  1. 我们之前在操作符里面就提到过,整数的2进制表示方法有三种,即原码,反码和补码。详细请看:操作符------原码反码和补码
    注意正整数的原,反,补码都相同;负整数的三种方法各不相同
  2. 那么对于整数来说,数据存放内存中其实存放的就是二进制的补码 的原因如下:
    使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是不相同的,不需要额外的硬件电路

二.大小端字节序和字节序判断

1.大小端

超过一个字节的数据在内存中存储的时候就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储。用图举个例子:

  • 大端(存储)模式:是指数据的低位字节内 (如上图中的6) 保存在内存的 地址处,而数据的高位字节内容 (如上图中的1)保存在内存的地址处
  • 小端(存储)模式:是指数据的低位字节内容 保存在内存的 地址处,而数据的高位字节内容 保存在内存的 地址处
    如下面在内存的存储就是小端字节序存储

2.大小端存在的原因

计算机系统是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit位。但是C语言中还有16bit的short型等,另外对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大小端存储模式

3.例题

  1. 设计一个小程序来判断当前机器的字节序:
c 复制代码
#include<stdio.h>
int check_sys()
{
	int a = 1;
	return (*(char*)&a);
}
int main()
{
	if (check_sys()==1)
	{
		printf("大端\n");
	}
	else
	{
		printf("小端\n"); 
	}
	return 0;
}
  1. 代码打印结果

我们首先需要知道一个知识点:

signed char的取值范围:-128~127

unsigned char的取值范围:0~255

同理:

signed short(16bit)取值范围:-32768~32767

unsigned short:0~65535

c 复制代码
#include<stdio.h>
int main()
{
	char a = -1;
	//原:10000000 00000000 00000000 00000001
	//反:11111111 11111111 11111111 11111110
	//补:11111111 11111111 11111111 11111111
	//保留低位:11111111 -a
	//整型提升:11111111 11111111 11111111 11111111
	signed char b = -1;
	unsigned char c = -1;
	////原:10000000 00000000 00000000 00000001
	//反:11111111 11111111 11111111 11111110
	//补:11111111 11111111 11111111 11111111
	//保留低位:11111111 -c
	//整型提升(补码):00000000 00000000 00000000 11111111
	//对应255
	printf("a=%d,b=%d,c=%d", a, b, c);//%d是以十进制的形式打印有符号的整数
	return 0;
}
  1. 求代码打印结果

%u是以十进制的形式打印无符号的整数------内存中存储的是无符号数的补码

c 复制代码
#include<stdio.h>
int main()
{
	char a = -128;
	//原10000000 00000000 00000000 1000000
	//反11111111 11111111 11111111 0111111
	//补11111111 11111111 11111111 1000000
	//按低位截断:10000000-a
	//按原来的类型进行整型提升后
	//补码为11111111 11111111 111111111 10000000
	//因为打印的无符号整数,所以会把上面这个数当成无符号整数,所以其原码和补码相同,所以打印的为十进制数就为4294967168
	printf("%u\n", a);
	return 0;
}

同理:

c 复制代码
#include<stdio.h>
int main()
{
	char a = 128;
	//128的原00000000 00000000 00000000 10000000
	//反00000000 00000000 00000000 10000000
	//补00000000 00000000 00000000 10000000
	//存到a里面去需要截断:100000000-a
	//存到a中的与上面例题中一样 
	//因为char有符号整型提升后为
	//11111111 11111111 111111111 10000000
	printf("%u\n", a);
	return 0;
	//打印结果仍为4294967168
  1. 求代码打印结果
c 复制代码
#include<stdio.h>
int main()
{
	char a[1000];
	//-128~127
	int i;
	for (i = 0;i < 1000;i++)
	{
		a[i] = -1 - i;
	}
	//-1,-2,-3......-1000,但是我们要知道有符号字符型取值-128~127
	//所以我们现在来算-129的
	//原10000000 000000000 000000000 10000001
	//反11111111 111111111 111111111 01111110
	//补11111111 111111111 111111111 01111111
	//01111111-a[]
	//整型提升11111111 11111111 11111111 01111111
	//所以存到字符型数组里的数为127
	//-1,-2,-3.......-128,127,126......2,1,0
	printf("%d", strlen(a));
	//255
	return 0;
}
  1. 求代码打印结果:

(1)

c 复制代码
#include<stdio.h>
unsigned char i = 0;
//0~255
int main()
{
	for (i = 0;i <= 255;i++)
	{
		printf("%u:hello world\n",i);
	}
	return 0;
}
//该程序进入死循环,且i不会超过255

(2)

c 复制代码
#include<stdio.h>
#include<windows.h>
int main()
{
	unsigned int i;
	//>=0
	//当i为负数时,会被当成无符号整型来看,转换过来就是一个非常大的数
	for (i = 9;i >= 0;i--)
	{
		printf("%u\n",i);
		Sleep(100);
	}
	return 0;
}
//程序陷入死循环
  1. 求代码打印结果

%x是以16进制的形式打印数据

c 复制代码
#include<stdio.h>
int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = ( int* )(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	//ptr1[-1]==*(ptr1-1)
	return 0;
}

打印结果:

三.浮点数在内存中的存储

  1. 浮点数家族包括:float,double,long,double类型
  2. 浮点数表示的范围:float.h中定义

例如:十进制的浮点数:8.0 / 9.5

先转换为二进制的浮点数:1000.0 / 1001.1

写成标准形式就是:(-1)^ 0* 1* 2^3 /

(-1) ^ 0*1.0011 *2^3

  1. IEEE 754还规定:
    对于32位的浮点数(float),最高1位的存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M;
    对于64位的浮点数(double),最高1位的存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

浮点数存的过程

IEEE 754对有效数字M和指数E,还有一些规定: 由于M>=1且M<2,所以M可写成1.xxxxxx的形式,xxxxxx表示小数部分。但其规定,计算机内部保存M时,默认这个数第一位总是1,因此可以被舍去,这是为了节省1位有效数字。
对于指数E(无符号整数): 若E为8位,则取值范围0 ~255;为11位,取值范围为0 ~2047。但科学计数法中的E可以出现负数,所以IEEE

754规定:存入内存时E的真实值必须再加上一个中间数,对于8位数E,中间数就是127;对于11位E,中间数是1023。

例:2^10的E是10,保存为32位浮点数为10+127=137,即10001001

设计虽精妙,但有时候浮点数有可能是无法精确保存的,如十进制数字1.2转换为二进制,小数点后有很多位,在VS调试后可能会有些许误差

例:

浮点数取的过程

从内存中取E时有三种情况:

  • E不全为0或不全为1(常规)
    指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1
  • E全为0
    此时,浮点数的指数E等于1-127(或1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示正负0,以及接近于0的很小数字
  • E全为1
    这时,如果有效数字M全为0,表示正负无穷大(正负取决于符号位s)。
  1. 例题:
c 复制代码
#include<stdio.h>
int main()
{
	int n = 9;
	//00000000 00000000 00000000 00001001
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);//9
	printf("pFloat的值为:%f\n", *pFloat);//0.000000
	//*pFloat会把9------>0 00000000 00000000000000000001001当成浮点数来解读
	//E全为0
	//标准形式(-1)^0*0.0000000000000000000000000001001*2^-126
	//这个数无限接近于0,且float保留六位小数
	*pFloat = 9.0;
	//以浮点型内存存储解读一下
	//二进制1001.0
	//标准形式(-1)^0*1.001*2^3
	//S=0
	//E=3
	//M=1.001
	//01000001000100000000000000000000
	//直接被认为补码转换一下
	//1091567616
	printf("n的值为:%d\n", n);
	printf("pFloat的值为:%f\n", *pFloat);//9.0

打印结果:

本次内容到这里就结束了,谢谢观看!

相关推荐
zhysunny15 分钟前
Day22: Python涡轮增压计划:用C扩展榨干最后一丝性能!
c语言·网络·python
YxVoyager18 分钟前
【C标准库】详解<stdio.h>标准输入输出库
c语言·c++
特立独行的猫a7 小时前
C/C++三方库移植到HarmonyOS平台详细教程(补充版so库和头文件形式)
c语言·c++·harmonyos·napi·三方库·aki
zh_xuan8 小时前
LeeCode 40.组合总和II
c语言·数据结构·算法
艾莉丝努力练剑9 小时前
《递归与迭代:从斐波那契到汉诺塔的算法精髓》
c语言·学习·算法
小马学嵌入式~17 小时前
数据结构:队列 二叉树
c语言·开发语言·数据结构·算法
KeithTsui19 小时前
GCC C语言整数转换的理解(Understanding of Integer Conversions in C with GCC)
c语言·开发语言·算法
jiunian_cn19 小时前
【Linux】线程
android·linux·运维·c语言·c++·后端
泽虞20 小时前
《LINUX系统编程》笔记p3
linux·运维·服务器·c语言·笔记·面试