C语言之数据在内存中的存储

  前言:我们在写代码的过程中,你是否会产生以下疑问:有符号数据和无符号数据的区别在哪里?为什么signed char的范围是-128~127?浮点数的存储是怎么样的呢?浮点数的存储和整型是一样的吗?如果你也有以上疑问,那么就来看看这篇文章吧。


这里是OMGmyhair的博客

如果你想要了解更多的内容可以看看我的主页OMGmyhair-CSDN


我将讲解一下三种数据类型:

 


目录

 一、整数在内存中的存储

1.正负数在内存中的存储形式

2.大小端

二、字符型在内存中的存储

1.unsigned char

2.signed char

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

1.浮点数的存储形式

2.浮点数的存取 


 一、整数在内存中的存储

首先,整数类型分为两类有符号类型和无符号类型

在C语言中,整数类型又有不同的大小,有long长整型、short短整型等。

整数类型表示范围可以在<limits.h>中查看。

接下来我们来具体看看整数在内存中存储:

整数在内存中是以二进制的补码形式存放。

1.正负数在内存中的存储形式

例如数字4,我们来看看它的原码、反码、补码。

正数的原码、反码、补码相同。

原码:00000000 00000000 00000000 00000100

反码:00000000 00000000 00000000 00000100

补码:00000000 00000000 00000000 00000100

00000000 00000000 00000000 00000100就是数字4在内存中的表示形式。

我们再来看看负整数在内存中的表示形式:

例如数字-4,我们来看看它的原码、反码、补码。

因为其是负数,所以符号位也就是最高位为1,值得注意的一点是:负数在取反时,要注意符号位不变,其它位按位取反。

原码:10000000 00000000 00000000 00000100

反码:11111111   11111111   11111111   11111011

补码:11111111   11111111   11111111   11111100

11111111   11111111   11111111   11111100就是数字-4在内存中的表示形式。

2.大小端

了解了整数在内存中的存储形式之后,我们再来看一个问题,即大小端问题。

在存储数据的时候,是将数据的高位字节存放在高位地址还是存放在低位地址呢?

大端字节序:高位字节放在低地址,低位字节放在高地址。

小端字节序:低位字节放在低地址,高位字节放在高地址。

我们来用图片讲解的形式来进一步理解,这里为了方便,我使用十进制数字进行举例。

 例如1234,1为高位,4为低位:

大端字节序:

小端字节序:

这里,我们也可以写代码对电脑大小端进行确认:

这里我将给出两种方法(都在VS上实现,VS采用的是小端字节序):

第一种方法:

int main()
{
	int n = 0x01020304;
	char a = *((char*)&n);
	printf("%d", a);
	return 0;
}

运行后:

 第二种方法:

union end
{
	int a;
	char b;
};
int main()
{
	union end u;
	u.a = 0x01020304;
	printf("%d", u.b);
	return 0;
}

运行后:


二、字符型在内存中的存储

现在我们已经明了了大小端以及如何去确认。接下来我们来看看字符型数据:

char类型取值范围
signed char-128~127
unsigned char0~255

 注:char是有符号的char还是无符号的char,取决于编译器。

这里我们来探讨一下unsigned char和signed char的取值范围。

1.unsigned char

首先char是一个字节,8个比特位,我们来列出unsigned char的取值范围:

2.signed char

接下来我们再来看看signed char,首先signed char是有符号的类型,则首位成了符号位:

注:-128->原码:11000 0000        反码:10111 1111           补码:11000 0000

但由于char只能存储8个比特位,发生截断,则-128在char中是1000 0000


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

浮点数表示范围可以在<float.h>中查看。

类型最小正值最大值精度
float1.17549x10^(-38)3.40282x10^386个数字
double

2.22507x10^(-308)

1.79769x10^308

15个数字

这里我们有两种主要的浮点数格式:单精度(32位)和双精度(64位)。数值以科学计数法的形式存储,每一个数都由三部分组成:符号、指数和小数。

1.浮点数的存储形式

我们来具体查看这三部分:

浮点数在二进制中被分为了三部分:V=(-1)^s*M*2^E

首先来看符号位(-1)^s:

当浮点数为正数的时候,S=0;当浮点数为负数的时候,S=-1;

M是二进制浮点数的有效数字。

2^E是二进制浮点数的指数位。

对于32位浮点数float,最高1位存储S,接着8位存E,剩下23位存储有效数字M。

对于64位浮点数double,最高1位存储S,接着11位存E,剩下52位存储有效数字M。

我们来拿9.5这个数字进行举例转换。

9.5在二进制中是1001.1

而1001.1->(-1)^0*1.0011*2^3.

默认M1.XXXX的数字,所以存储的时候我们只存小数点后面的数字,即XXXX,读取的时候再加上首位的1,这样可以提高精度。

而E会出现有负数的情况,为了解决这一问题,存入时E的真实值必须加上一个中间值,8位的E的中间值是127;11位的E的中间值是1023。

注意:还有一些特殊情况,

当E全为0的时候,则E=1-127或者1-1023,有效M不再加上第一位的1,而是还原为0.XXXX的小数。

当E为全1的时候,这时若有效M全为0,则表示±无穷大。

2.浮点数的存取 

接下来我们用VS进行举例讲解:

int main()
{
	int a = 5;
	float* ptf = (float*)(&a);
	printf("a的值为:%d\n", a);
	printf("*ptf的值为:%f\n", *ptf);
	*ptf = 5.0;
	printf("a的值为:%d\n", a);
	printf("*ptf的值为:%f\n", *ptf);
	return 0;
}

拿这个代码来分析:

a在内存中的存储为00000000 00000000 00000101;

而ptf指针认为自己指向的是一个浮点数,于是它会将上面的二进制形式以浮点数存储来看待:

0  00000000  000000000000101

此时S=0,E为全0,所以读取的时候,E=1-127,有效数字M不加1,就是0.000000000000101的形式。

还原为浮点数科学计数法的形式为:

V=(-1)^0*1.01*2^(-139)

这是一个非常接近0的数字,而%f默认打印小数点后面6位,所以此时输出*ptf会是0.000000;

当*ptf=5.0时,a的地址存储的是浮点数在二进制中的表现形式:

5.0->101->101=(-1)^(0)*1.01*2^2;

S=0,M=01,E=2+127->10000001;

则它在内存中的存储形式如下:

即01000000101000000000000000000000

此时打印a的话,a会认为自己内存中存的是整数二进制的补码,即将01000000101000000000000000000000看作一个整数的二进制,此时输出为1084227584;

这时候我们的分析结束,走一遍代码,查看是否正确:



至此,我们已经了解了这些数据在内存中是怎么存储的。

如果这篇文章有帮助到你,请留下您珍贵的点赞、收藏+评论,这对于我将是莫大的鼓励!学海无涯,共勉!



  • 45
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值