前言
C语言中有很多种数据类型,本文将具体介绍这些数据到底是如何储存在内存中的,以及这样储存的好处~
一、数据在内存中是如何储存的?
-
整数在内存中的储存
整数的二进制表示方法有三种——原码,反码,补码(均由符号位和数值位两部分构成)
规律:正整数的原反补均相同,负整数三种各不同
原码:直接将数值按照正负数的形式翻译成二进制
反码:除最高位符号位外将其他位次按位取反
补码:在反码的基础上加1
重要关系:原码——取反+1——>补码(这两者之间可以互相转换)
<——、、、——
结论:整数直接以补码的形式在内存中储存
原因:由上述的重要关系可得,原码和补码之间的转换是相同的,不需要额外的硬件电路
-
大小端字节和字节序的判断
1.什么是大小端?
大端储存:将数据的低(高)位字节内容保存在高(低)地址处
小端储存:将数据的低(高)位字节内容保存在低(高)地址处
易知地址的小是从左往右,由低到高,该图中将将数据a的地址按小端储存。
-
浮点数在内存中的储存
我们可以先做一下下面的联系,想想会打印出什么?
#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;
}
结果如下:我们发现同样的数字,将整型赋值给浮点型和将浮点型赋值给整型的结果均不相同,由此我们可以推断出整型和浮点型在内存中的储存方式不同,那么浮点型是如何储存的呢?
根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1) ^S* M * 2^E
•(−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数
•M 表⽰有效数字,M是⼤于等于1,⼩于2的(因为是二进制所以不可能超过2,又要用科学计数法表示所以M>1)
•E 表⽰指数位
举例来说:(类似于科学计数法)
⼗进制的5.0,写成⼆进制是 101.0 ,相当于 1.01×2^2 。
那么,按照上⾯V的格式,可以得出S=0,M=1.01,E=2。
⼗进制的-5.0,写成⼆进制是 -101.0 ,相当于 -1.01×2^2 。那么,S=1,M=1.01,E=2。
IEEE 754规定:
其中我们规定E=0时,E=1-127;E全为1时,如果M=0,则表示无穷大
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
由上图我们可知为什么会有双精度浮点型和单精度浮点型,因为有64位的双精度浮点型有52位储存有效数字易知其精度要远大于32位
此时我们再自己思考一下上面的练习为什么会有这样的结果。
二、C语言的内存函数
1.memcpy函数
作用:将源函数指定的数据复制到目标函数中
注意:
- 这个函数在遇到'\0'时不会停下来
- 如果源函数与目标函数有任意的重叠都是未定义的
- num是字节数而不是元素的个数
#include <stdio.h>
void* my_memcpy(void* dest, const void* src, size_t num)//只要实现不重叠字符的覆盖就好了
{//void类型的指针不能够直接解引用使用,为了函数的普适性,我们应该一个字节一个字节的复制,so应该强制转化为char*类型
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 11,12,34,56,78 };
my_memcpy(arr1, arr2, 20);//可以不接受任何返回值
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
如图所示,将arr2中的20个字节所对应的数字复制到了arr1中
2.memmove函数
memmove函数的功能更加强大能够处理重叠部分的复制
由图可知重叠复制与dest和str的位置关系有关,我们只要根据他们的位置关系进行复制即可,不重叠两种方式均可
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t num)//传输的num是字节数
{
assert(dest && src);
void* ret=dest;
if (dest > src)//地址也是数字是可以比较的
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);//从前向后复制
}
}
else {
while (num--)
{
*(((char*)dest)++) = *(((char*)src)++);//转化成字符型指针再进行自增
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1, arr1+3, 20);//可以不接受任何返回值
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
总结
数据在内存中的储存虽然基础但相当重要并且颇有学问,本文讲解了有关数据储存更深入的知识,敬请期待下一章的练习!