【C语言】数据存储篇,内存中的数据存储----C语言整型,浮点数的数据在内存中的存储以及大小端字节序【图文详解】

欢迎来CILMY23的博客喔,本篇为​【C语言】数据存储篇,内存中的数据存储----C语言整型,浮点数的数据在内存中的存储以及大小端字节序【图文详解】,感谢观看,支持的可以给个一键三连,点赞关注+收藏。 

 前言

C语言中整型的存储是原码反码补码,那么浮点数的存储又是什么样的呢?本篇将以整型的数据存储开始,带大家了解C语言中数据的存储。

目录

 一、整型的存储

二、大小端字节序

三、练习题

四、浮点数的数据存储


 一、整型的存储

在C语言中,整型数据是以二进制的形式存入内存中的,那在c语言中能对二进制进行操作的有几个操作符,~ ,&,|,^,<<,>>。整数的二进制表示⽅法有三种,即原码、反码和补码,三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位最高位的⼀位是被当做符号位,剩余的都是数值位。

   符号位:

正整数的原码反码补码都相同

例如:4

它的原码,反码,补码都为

00000000 00000000 00000000 00000100

负整数的原码反码补码则表示不一样
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。 

例如以下负四这个例子: 

对于整形来说:数据存放内存中其实存放的是补码
为什么呢?
在计算机系统中,数值⼀律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统⼀处理;
同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

二、大小端字节序

虽然整型数据是按二进制存入内存的,但是在vs的编译器上却采用的是十六进制表示。

例如:

这是整型四的表示,在内存中的地址是 0x00CFFC8C

但是为什么vs的表示是04在前面的呢? 

这就涉及大小端字节序了,大小端字节序分大端字节序和小端字节序,字节序说的就是字节的顺序,当一个字节超过一个数值大小的时候,就有存储的顺序问题。内存中的存储单元是1字节的。

 大端字节序,是指数据的低位字节内容保存在内存的⾼地址处,而数据的高位字节内容,保存在内存的低地址处。

小端字节序,是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。

vs采用的是小端字节序存储

三、练习题

 3.1 如何判断机器的大小端字节序?

 思路一、通过1的二进制来判断,如果位于低地址处的1字节是01,那么是小端字节序,如果位于低地址处的1字节是00.那么是大端字节序。

    int a = 1;
    if (*(char*)&a == 1)
	{
		printf("小端字节序");
	}
	else
	{
		printf("大端字节序");
	}

 图解如下:

 思路一优化:我们可以根据(char*)&a的值进行封装函数,解引用后得到1就是1,得到0就是零,

#include<stdio.h>

int check_sys()
{
	int a = 1;
	return (*(char*)&a);
}

int main()
{
	
	if (check_sys() == 1)
	{
		printf("小端字节序");
	}
	else
	{
		printf("大端字节序");
	}
	return 0;
}

 3.2练习题1

#include <stdio.h>

int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d", a, b, c);

	return 0;
}

 答案是:

解析: char类型比较特殊,我们没法单纯通过观察判断char类型到底是signed,还是unsigned,而在vs上,它是被认为成signed char的。 

首先char a 的内部是什么样的呢?如下图所示:

b其实也同理可得,其中存放的是八个1,那c也是如此,只是因为没有了符号位,所以这里的三个变量都存了八个1.

%d是以十进制的形式打印有符号整型,所以会对a进行整型提升,也就是将其补充成四个字节的数据

由于b 和a一样都是signed char类型,所以过程也一样

而c不一样,c由于是无符号整型

因此打印的时候是-1,-1,255

 3.3练习题2

#include <stdio.h>
int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}

%u打印无符号整数 

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

  答案如下:

补充:

char类型的取值范围,char一个字节对应八个比特位,而这八个比特位的取值又如下 所示,所以有符号的char类型取值是-128-127.

所以a当中存储的是

00000000000000000000000010000000----补码

11111111111111111111111110000000----原码

故答案为4294967168

3.4练习题3


#include<stdio.h>

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
    {
	  a[i] = -1 - i;
	}

	printf("%d", strlen(a));
	return 0;
 }

 根据前面所说,其实signed类型是在兜圈子

 所以这里只要确定0的位置即可,a当中从-1开始存,存够255之后又开始新的轮回

 3.5练习题5

#include<stdio.h>

unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
                        
	return 0;
 }

结果是死循环 

解析:

无符号char取值是0~255,所以会无限制的打印

3.5练习题4 

#include <stdio.h>

int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}

	return 0;
}

解析:

i的数据类型是unsigned int ,是恒大于等于0的,而循环的条件是i>=0,恒成立,所以会不限制的答应i的值。会从9一直开始打印,然后进入2^ 0~2^32-1. 0~4294967295

3.6练习题5 

#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);

	return 0;
}

结果如下:

解析:

a是数组名,单独放在&后面,取出的是整个数组的地址,+1跳过整个数组的大小,(不理解的可以看http://t.csdnimg.cn/BlJ00)指向末尾,将其存入ptr1, ptr[-1] == *(ptr-1),将其解引用后得到的就是4。

因为数据在vs上看是小端存放,本身转换成int类型后,就是a本身的地址直接加1,然后将其转换成int*类型是四个字节,解引用后,读取要反过来,所以是20 00 00 00

四、浮点数的数据存储

常⻅的浮点数:3.14159、1E10等,浮点数家族包括:  float、double、long double 类型。

看一个例子:

#include <stdio.h>

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

结果如下:

那为什么会这样呢?

这就涉及浮点数的存储了。

假设

V = 5.625 

V = 101 .101 -----二进制

V = 1.011 * 2 ^2

V = (-1) ^ 0 * 1.011 * 2^2 

 其实就是先转换成二进制,然后将二进制小数点向左移动一位的同时E+1,最后将二进制化成大于等于1小于2的数。

 那根据这样的化简,我们有两种存储方式

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

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

 因为最终M都会化简成1...多少的形式,所以我们只需要在内存中存储有小数点的位置即可。

我们接着看是如何存和取的:

我们存入的时候将其转换成二进制,

所以我们就可以解释第一个练习了:

 9以整型的形式存储在内存中,得到如下二进制序列:

0000 0000 0000 0000 0000 0000 0000 1001

首先,将9的二进制序列按照浮点数的形式拆分,得到第⼀位符号位s=0,后面8位的指数 E=00000000 ,
最后23位的有效数字M=000 0000 0000 0000 0000 1001。
由于指数E全为0,因此,浮点数V就写成:
    V=(-1)^0 × 0.00000000000000000001001×2^(-126)=1.001×2^(-146) 显然,V是⼀个很小的接近于0的正数,所以用十进制小数表⽰就是0.000000。

浮点数9.0,为什么整数打印是 1091567616
首先,浮点数9.0等于二进制的1001.0,即换算成科学计数法是:1.001×2^3
所以:9.0   =   (−1) ^0 * (1.001)*2^3,
那么,第⼀位的符号位S=0,有效数字M等于001后面再加20个0,凑满23位,指数E等于3+127=130, 即10000010
所以,写成二进制形式,应该是S+E+M,即

0 10000010 001 0000 0000 0000 0000 0000 

这个32位的⼆进制数,被当做整数来解析的时候,就是整数在内存中的补码,原码正是
1091567616

感谢各位同伴的支持,本期数据存储篇就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞关注+收藏,若有不足,欢迎各位在评论区讨论。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值