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