内存函数
使用内存函数需要包含头文件 <string.h>
memcpy
将内存的数据拷贝到新空间
void* memcpy(void* destination, const void* source, size_t num);
- memcpy函数能将源地址后num个字节的数据拷贝到目标空间
- 区别内存函数和字符串函数,memcpy拷贝时不会像strcpy那样遇到\0停下
- 返回值为目标空间的地址,接收时通常要进行强制类型转换
#include <stdio.h>
#include <string.h>
int main()
{
int a1[10] = {1,2,3,4,5,6,7,8,9,0};
int a2[10] = {0};
memcpy(a2, a1, 5 * sizeof(int));
for(int i = 0; i < 10; i++)
{
printf("%d ",a2[i]);
}
//打印结果是:1 2 3 4 5 0 0 0 0 0
return 0;
}
模拟实现
void* My_memcpy(void* des, const void* src, size_t num)
{
assert(des && src);
void* tmp = des;
while(num--)
{
*((char*)des)++ = *((char*)src)++;
}
return tmp;
}
我们在拷贝时可能会遇见源空间和目标空间重叠的情况,这时候有一个专门的函数就是memmove
memmove
同样具有拷贝的作用,不过memmove被用来处理有内存重叠的拷贝
void* memmove(void* destination, const void* source, size_t num);
#include <stdio.h>
#include <string.h>
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,0};
memmove(a+2, a, 5 * sizeof(int));
for(int i = 0; i < 10; i++)
{
printf("%d ",a[i]);
}
//打印结果是:1 2 1 2 3 4 5 8 9 0
return 0;
}
其实我们用memcpy也可以实现上述结果,不过我们习惯用memmove函数来处理有内存重叠的拷贝。memcpy函数算是有了超出我们对它预期的能力。
模拟实现
void* My_memmove(void* des, const void* src, size_t num)
{
assert(des && src);
void* tmp = des;
if (des < src)//des位于低地址处,src位于高地址处
{
while (num--)
{
*((char*)des)++ = *((char*)src)++;
}
}
else//des位于高地址处,src位于低地址处
{
while(num--)
*((char*)des + num) = *((char*)src + num);
}
return tmp;
}
解析
我们在模拟实现有内存重叠的拷贝函数时,要解决的最大问题是:怎样避免将未拷贝的数据提前覆盖,导致输出出错?
我们画图思考:
首先我们清楚,我们拷贝时是一个字节一个字节拷贝的。
- 如果是这种情况:des位于低地址处,src位于高地址处,我们采用从头往后拷贝,即:1 = 3、2 = 4 、3 = 5、4 = 6、5 = 7,没有出现未拷贝数据提前覆盖的情况。
-
如果是这种情况:des位于高地址处,src位于低地址处,我们再次采用从头往后依次拷贝,3 = 1、4 = 2进行到这里,我们发现3的位置已经拷贝成1了,出现了未拷贝数据提前覆盖的情况。
不妨我们尝试从后往前拷贝:7 = 5、6 = 4、5 = 3、4 = 2、3 = 1,非常满意的结果。
memset
设置内存空间
void* memset(void* ptr, int value, size_t num);
- ptr要设置空间的起始地址
- num是字节数,表示我们要设置ptr向后num个字节的内存空间。
- value是每个字节的内存空间的设置值
- 返回值是ptr
#include <stdio.h>
#include <string.h>
int main()
{
char a[] = "hello wenhao!";
char* p = (char*)memset(a, 'a', 13);
printf("%s\n", p);
}
//打印结果是:aaaaaaaaaaaaa
看了这个例子我们发现memset具有初始化数组的能力。
//我们的目的是:将整型数组a中的每个元素都初始化为1
#include <stdio.h>
#include <string.h>
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
memset(a, 11, sizeof(int) * 10);
for (int i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
return 0;
}
上面的例子打印结果是:
为什么初始化后的结果不是我们期望的?
因为memset是以字节为单位设置内存空间的,上述例子是将a向后40个字节的内存空间都设置成1了,整型数组的每个元素占4个字节,32个bit位,memset后,每个元素所占的四个字节内存空间存储的二进制序列是00000001 00000001 00000001 00000001
而这个数就是我们打印的16843009
不过,我们可以给memset的第二个参数传入0,将整型数组每个元素都初始化为0,因为这时候设置后的每个元素的二进制序列是:
00000000 00000000 00000000 00000000
模拟实现
void* My_memset(void* ptr, int val, size_t num)
{
assert(ptr);
void* tmp = ptr;
while (num--)
{
*((char*)ptr)++ = val;
}
return tmp;
}
memcmp
比较每个字节内存空间的数据
int memcmp(const void* ptr1, const void* ptr2, size_t num);
-
比较ptr1和ptr2后num个字节的内存空间,与strcmp类似,是逐个字节比较,如果要比较20个字节空间,在比较第17个字节时分出大小,则会返回对应值(>0 或 <0),只有20个字节全相等,才会返回0。
-
返回值:
举例
#include <stdio.h>
#include <string.h>
int main ()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n = memcmp ( buffer1, buffer2, sizeof(buffer1));
if (n>0)
printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0)
printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else
printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
return 0;
}
//打印结果是:'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.