内存操作相关函数
这类函数主要在内存中修改数据。
memcpy
存在于string函数库中,其源码如下所示
void * __cdecl memcpy(void * __restrict__ _Dst,const void * __restrict__ _Src,size_t _Size)
其主要的功能和作用是:用src区里的数据覆写dst区的数据。
其主要工作方式如下
//功能
//形参1:目的地址
//形参2:来源地址
//形参3:需要拷贝的字节数量,特别需要注意考虑内容的类型,进行必要的计算(总空间=数据类型空间占有量*元素数)
memcpy(b,a,8);
数组之间的拷贝
{
int a[5]={-10,-20,30,40,50}; //int类型,一个元素是4个字节
// 00 00 00 00 一个字节最大值FF
// 我们这种是小端模式
// 10 00 00 00 还是 00 00 00 10 根据编译器及系统决定是小端模式,还是大端模式
print(a,5);
printf("a数组的首地:%#p\n",a);
char *p=&a;
printf("a数组的首地:%#p\n",p);
int i;
for(i=0;i<8;i++)
{
printf("p内容%#p ",(p+i));
printf("*p内容%x\n",*(p+i));
}
int b[5]={0};
memcpy(b,a,8);
print(b,5);
}
输出为
其中,单个整型元素的大小为4byte,程序中size为8byte,所以从b[0]有2个整形元素被覆写。
*使用memcpy函数时,特别要注意数据长度。如果复制的数据类型是char,那么数据长度就等于元素的个数。而如果数据类型是其他(如int, double, 自定义结构体等),就要特别注意数据长度的值。
建议使用 n * sizeof(type_name)
的写法。
重叠拷贝(数组元素在数组内部拷贝)
若输入的size不足以被数据类型占有空间整除会发生什么?
明明pa[2]只保留了前2个字节,却还是覆写了b[2]。
这里又会涉及到big-endian和little-endian的问题。假设是小端方式储存(更常见),那么读到的是元素1的低8位,写成十六进制即0x1。
由于指针加减常数,单位是与类型保持一致的,也就是在a的基础上,增加3倍int长度,对应的是元素3的地址。b2被替换为0。元素4写成十六进制是0x0004,低8位被替换为0x1,变为0x0001。
因此b2会被覆写。
数组内部拷贝
{
int a_04[10]={-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
print(a_04,10);
//拷贝元素0 、1 到元素 8 、9的位置
//结果:int a_04[10]={-1,-2,3,4,5,6,7,-1,-2}
//memcpy(&a_04[8],&a_04[0],4);
//利用memcpy函数,实现同一数组内,内容的拷贝,不交叉
// memcpy(a_04+8,a_04,8);
// print(a_04,10);
//拷贝元素0 、1 、2到元素 1 、2 、3的位置 往后拷贝
//结果:int a_04[10]={-1,-1,-2,-3,-5,-6,-7,-8,-9,-10}
//*****memcpy在其他平台上,可能存在只能从前向后拷贝*****
// memcpy(a_04+1,a_04,12);
// print(a_04,10);
//拷贝元素0 、1 、2到元素 1 、2 、3的位置 往前拷贝
//结果:int a_04[10]={-3,-4,-5,-4,-5,-6,-7,-8,-9,-10}
// memcpy(a_04,a_04+2,12);
// print(a_04,10);
//利用memcpy函数,实现同一数组内,内容的拷贝,不交叉
// memmove(a_04+8,a_04,8);
// print(a_04,10);
//拷贝元素0 、1 、2到元素 1 、2 、3的位置 往后拷贝
//结果:int a_04[10]={-1,-1,-2,-3,-5,-6,-7,-8,-9,-10}
// memmove(a_04+1,a_04,12);
// print(a_04,10);
//拷贝元素0 、1 、2到元素 1 、2 、3的位置 往前拷贝
//结果:int a_04[10]={-3,-4,-5,-4,-5,-6,-7,-8,-9,-10}
memmove(a_04,a_04+2,12);
print(a_04,10);
}
memmove元素后移前移
相较于 strcpy函数 不会具有同样的局限性,但是 memcpy函数也是有其的巨大缺陷,其不能对本身进行覆盖拷贝,于是便有了 memmove函数 ,其是能够对本身进行覆盖拷贝的函数,其又同时兼备了 memcpy函数 可做的事。
memmove定义如下
memmove(a_04+8,a_04,8); //与memcpy相似,都是目的-起源-字节数的格式
void test04(void)
{
int a_04[10]={-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
print(a_04,10);
memmove(a_04,a_04+2,12);
print(a_04,10);
}
memset
memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。
其源代码如下图所示:
void * __cdecl memset(void *_Dst,int _Val,size_t _Size);
dst为目的地址
val为要填充的值(但填充的值大小有限:memset函数是按照字节对内存块进行初始化,所以不能用它将int数组出初始化为0和-1之外的其他值(除非该值高字节和低字节相同)。
其实c的实际范围应该在0~255,因为memset函数只能取c的后八位给所输入范围的每个字节。也就是说无论c多大只有后八位二进制是有效的。一旦超过这个范围数据就失去控制。)
size为数据占有的空间
{
char a_05[5]={0};
memset(a_05,'c',4);
print_c(a_05,5);
//数据存储
int a_25[5]={0};
memset(a_25,4626,4); //测试若填充数爆表后会发生什么
print(a_25,5);
}
打印结果(其中上半部分为a05的填充,下半部分为a25的填充)
其中从a25[0]的填充数与4xxx差距明显,表明该指令对填充数大小有限制
memcmp
这项指令旨在比较两个同类型数组前数个存储单元内数据的大小,该数组按字节大小比较。
指令源代码格式如下
int __cdecl memcmp(const void *_Buf1,const void *_Buf2,size_t _Size);
memcmp(a_06,b_16,7);
其中buf1 buf2为参与比较的两个数组,size为数据占有的空间大小
当buf1<buf2时,返回值<0
当buf1=buf2时,返回值=0
当buf1>buf2时,返回值>0
例:
{
int a_06[]={10,1315602,13,14,15};
int b_16[]={10,1381138,22,23,24};
//a_06[0] 10 00 00 00
//a_06[1] 12 13 14 00
//b_16[0] 10 00 00 00
//b_16[1] 12 13 15 00
int ret=10;
ret=memcmp(a_06,b_16,7);
printf("ret = %d\n",ret);
}
如何比较大小?
对于整型和浮点型数组,只需比较数组内各元素大小即可。对于字符串,先把元素转换为ascⅡ的整数再逆序比较。
sizeof(测量)的易错点
(&a[0]+1) 以二维数组a的第0行地址为起点下移一行,即行1的头地址
(a[0]+1) 在a[0]行内,从a[0][0]开始取行内下一个元素的地址。
(a+1) 取地址,从行0开始指向下一行
*a+1 指向下一行
*a 取元素a[0][0]的内容