C语言学习之数据在内存中的存储
今天我们来学习以下C语言中数据是如何在内存中存储的
目录
整数在内存中的存储
大小端和字节判断
为什么会有大小端之分呢?
练习
练习1
练习2:
练习3:
为什么char的存储范围是127到-128
练习4:
练习5:
练习6:
浮点数在内存中的存储
浮点数存储
浮点数存储的过程
浮点数的取出过程
E不全为0或1(常规)
E全为0
E全为1
返回之前的题目解析
整数在内存中的存储
在之前操作符的内容中,我们就了解过整数在内存当中的存储,接下来我们来简单总结一下吧。
整数的2进制表示方法有三种,即原码、反码、补码。
有符号的整数,三中表示方式均有符号位和数值位,符号位都是用0表示“正”,用1表示“负”。最高一位当做符号位,其余均为数值为。
即:
正整数的原、反、补码均相同。
负整数的原、反、补码各不相同
原码:直接将数值位按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反即可。
补码:反码+1就得到补码
对于整形来说:数据存储在内存之中其实存放的是二进制的补码。因为计算机系统数值一律用补码来表示和存储,使用补码可以将符号位统一处理。同时加法和减法统一处理(CPU只有加法器)。此外,补码和原码相互转换,运算过程是相同的,不需要额外的电路。
大小端和字节判断
#include<stdio.h>
int main()
{int a=0x11223344;return 0;
}
以上这段代码经过调试后我们会发现这样一个细节,a的数据是倒着存储的
其实超过以恶搞字节的数据在内存中村塾的时候,就有储存顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储。
大端(存储)模式:
指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容保存在内存的低地址处。
小端(存储)模式:
指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容保存在内存的高地址处。
具体可以参考以下这个图
为什么会有大小端之分呢?
计算机系统中我们以字节为单位,每个地质单元对应一个字节,一个字节对应8bit位。但是C语言中除了1字节的char外,还有2字节的short、4字节的int等等。对于位数大于8的处理器。由于寄存器宽度大于一个字节,必然存在着一个如何将多个字节安排的问题。因此导致了大端存储模式和小段存储模式。
我们常用的X86和X64是小端模式,而KEIL、C51 是大端。很多ARM、DSP都是小端字节序模式。某些ARM可以由硬件决定小端还是大端。
练习
练习1
设计一个小程序判断当前机器的字节序:
思路: 取出存储数据的第一个字节。以1 为例,若取出的第一个字节为1则为1则为小端,反之则为大端
取这个字节可以用char*的指针来判断
方法一:
#include<stdio.h>
int check_sys()
{int i = 1;return (*(char*)&i);
}
方法二:
int check_sys()
{union{int i;char c;}un;un.i = 1;return un.c;
}
int main()
{int sys = check_sys();if (sys == 1){printf("小端\n");}else{printf("大端\n");}return 0;
}
练习2:
#include<stdio.h>
int main()
{char a = -1;signed char b = -1;unsigned char c = 255;printf("a=%d,b=%d,c=%d\n",a,b,c);return 0;
}
结果为:
为什么会是这个结果呢?
char是否由符号取决于编译器
VS上char是有符号的。
-1的原码为:
1000000000000000000000000000001
-1的补码为:
11111111111111111111111111111111111
a存储的为:
11111111
在VS上,b的存储与a一样
由char转换为int打印需要整型提升
(整型提升的知识可以参考往期博客:C语言学习之操作符-CSDN博客)
a、b整型提升后为:
11111111111111111111111111111111111
c因为是无符号数,所以为:
0000000000000000000000111111111
练习3:
#include<stdio.h>
int main()
{char a = 128;signed char b = -128;printf("a=%u\nb=%u\n",a,b);return 0;
}
结果为:
-128的原码
100000000000000000000000010000000
-128的补码:
1111111111111111111111111111110000000
整型提升后:
1111111111111111111111111111110000000
因此打印出来后上图的结果
为什么char的存储范围是127到-128
练习4:
#include<stdio.h>
int main()
{char a[1000];for (int i = 0;i < 1000;i++){a[i] = 1 - i;}printf("%d " ,strlen(a));//strlen求得是字符串的长度,是\0之前的字符个数return 0;
}
结果为:
为什么呢?
相当于数值从-1到-128,然后跳到了0加到了127进行一次循环 。遇到0就是相当于’\0‘。
strlen函数计算的是到'\0'之前字符的个数
练习5:
#include<stdio.h>
unsigned char i = 0;
int main()
{for (i = 0;i <= 255;i++){printf("hello world \n");}return 0;
}
结果为:无限循环打印hello world
int main()
{unsigned char i = 0;for (i = 9;i >=0;i--){printf("%u \n");}return 0;
}
无限循环打印
练习6:
#include<stdio.h>
//X86环境,小端字节序
int main()
{int a[4] = {1,2,3,4};int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);//%x以16进制的形式打印数据printf("%x\n%x\n",ptr1[-1], ptr2);return 0;
}
结果为:
思路图如下:
浮点数在内存中的存储
常见的浮点数:3.1415、1E10等等。浮点数包括:float、double、long double类型
浮点数表示范围在:float.h中定义
#include<stdio.h>
int main()
{int n = 9;float* pf = (float*)&n;printf("n的数值为:%d\n",n);printf("pf的数值为:%f\n", *pf);*pf=9.0;printf("n的数值为:%d\n",n);printf("pf的数值为:%f\n", *pf);return 0;
}
结果为:
浮点数存储
在上面的代码中为什么n和*pf在内存中是一个数为什么浮点数和整数的差距这么大呢?这就需要我们去了解浮点数的存储原理
在国际标准IEEE(电气和电子工程协会)中,任意一个二进制浮点数V可以表示为以下形式:
举例说明:十进制的5.0写成二进制是101.0,相当于1.01x2^2
那么按照上面的格式,可以得出S=0,M=1.01,E=2
标准规定:
对于32位数的浮点数(float),最高的一位存储符号位S,接着是8位存储指数E,剩下的23位是存储有效数字M
对于64位的浮点数(double),最高的一位存储符号位S,接着是11位置存储指数E,剩下的52位存储有效睡着M
浮点数存储的过程
标准对有效数字M和指数E由一些特别规定
1<=M<2,也就是说M写成1.xxxxxxx
标准规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的xxxxxx部分。⽐如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的⽬的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保存24位有效数字。
E相对来说比较复杂
E是一个无符号整数。
因此,如果E是8位的数字,范围是0到255.如果是11位,则为0到2047。
但是E是可以出现负数的。因此标准规定,E的真实值必须加上一个中间数。
对于8位数的E,中间数是127;对于11位数的E,这个数是1023.比如2^10的E是10.因此保存成32位的时候必须保存为10+127,即10001001
浮点数的取出过程
E分为3钟情况
E不全为0或1(常规)
E的数值减去127(或者1023)得到真实值,再将有效数字M前加上第一位的1
比如:0.5的二进制的形式为0.1,正数部分必须为1.即将小数点右移1位,则为1.0*2(^-1),其阶码为-1+127=126.表示为01111110.尾数1.0去掉0,补齐到0到23位0.则二进制表示形式
1 0 011110 00000000000000000000
E全为0
E为1-127(或者1-1023)为真实值。M不再加上第一位的1,而还是还原为0.xxxxxxx的小数
未来表示+-0或者接近0的数字
0 00000000 001000000000000000000000
E全为1
M全为0,表示无穷大(正负取决于符号位)
0 11111111 0010000000000000000000000
返回之前的题目解析
为什么9还原为浮点数变成了0.000000?
有如下二进制序列:
1 0000 0000 0000 0000 0000 0000 0000 1001
后面的指数为E=00000000
M为0000 0000 0000 0000 0000 1001
由于E全是0.所以V=(-1)^0*0000 0000 0000 0000 0000 1001*2^(-126)=
1.001*2^(-146)
因此V是一个接近于0的正数,所以十进制为0.0000000
为什么浮点数9.0打印出来的整数是1091567616?
因为9.0 等于二进制1001.0,即为1.001*2^3
所以9.0=(-1)^0*(1.001)*2^3
所以第一位的符号位S=0,M为011后20个0.E为3+127=130,即10000010
写成二进制为S+E+M,即
0 10000010 001 00000000000000000000
本篇博客到这里结束了,求一个点赞,谢谢
封面图自取: