目录
一、memcpy() 函数的使用和模拟实现
函数原型:
// memcpy() 函数原型
void * memcpy ( void * dest, const void * src, size_t n);
函数功能:
该函数把指针 src 指向的内存区域的前 n 个字节拷贝到指针 dest 指向的内存区域的前 n 个字节。(跟 qsort() 函数类似,两个函数都是预先不知道传入的地址是什么类型,所以使用 void* 类型的指针接收。这样也方便拷贝任何类型的数据。)
注意事项:
(1)确保传入指针是合法的
(2)确保拷贝的字节数在合法范围内
(3)拷贝对应的两块内存空间不能重叠
(4)包含头文件 string.h
使用示例:
(1)正确示例
(2)错误示例,拷贝对应的两块内存出现重叠(由于 VS 下的 memcpy() 函数不能演示错误示例,这里使用的是作者模拟的。)
如果拷贝成功,那么打印结果应该是 1 2 3 1 2 3 4 5 9 0
模拟实现:
从两个传入的地址开始逐个字节拷贝,直到拷贝了 n 个字节。
// 模拟实现库函数 memcpy()
void* my_memcpy(void* dest, const void* src, size_t n)
{
// 存储目标地址
void* ret = dest;
// 开始逐个字节拷贝
int i;
for (i = 0; i < n; ++i)
*((char*)dest + i) = *((char*)src + i);
// 返回
return ret;
}
1. 为什么 memcpy() 不能处理内存重叠的情况
如下代码:
int arr1_i[10] = {
1,2,3,4,5,6,7,8,9,0 };
// 把 1 2 3 4 5 拷贝到 4 5 6 7 8
memcpy(arr1_i + 3, arr1_i, sizeof(int) * 5);
// 打印验证
int i;
for (i = 0; i < 10; ++i)
printf("%d ", arr1_i[i]);
printf("\n");
使用 memcpy() 函数,把 1 2 3 4 5 拷贝到 4 5 6 7 8。而 memcpy() 函数是从前往后依次拷贝的,如下图:
所以这就是为什么 memcpy() 函数不能处理内存重叠的情况,因为 memcpy() 函数在设计的时候是从前往后拷贝的。
二、memmove() 函数的使用和模拟实现
函数原型:

// menmove() 函数原型
void* memmove(char* dest, const char* src, size_t n);
函数功能:
该函数的功能和 memcpy() 函数一模一样,只不过当拷贝对应的两块空间出现重叠的时候,该函数也可以使用。
注意事项:
(1)保证传入的指针是有效的
(2)保证拷贝的字节数在合法范围之内
(3)包含头文件 string.h
使用示例:
拷贝对应的两块内存重叠:
模拟实现:
// 模拟实现库函数 memmove()
void* my_memmove(void* dest, const void* src, size_t n)
{
// 存储目标地址
void* ret = dest;
// 判断两个地址的前后顺序
if (dest < src)
{
// 从前往后拷贝
int i;
for (i = 0; i < n; ++i)
*((char*)dest + i) = *((char*)src + i);
}
else
{
// 从后往前拷贝
while (n--)
*((char*)dest + n) = *((char*)src + n);
}
// 返回目标地址
return ret;
}
1. 为什么 memmove() 可以处理内存重叠的情况
从上面模拟的 my_memmove() 函数中可以发现,该函数会先判断一下两个指针的大小关系,如果目标地址大,那么就从后往前拷贝;如果目标地址小,那么就从前往后拷贝。如下图:
这样就无论内存如何重叠,该函数都可以解决。(大家可以自己在纸上简单的演示一下,很容易验证)
三、memset() 函数的使用和模拟实现
函数原型:
// memset() 函数原型
void * memset ( void * ptr, int value, size_t num );
函数功能:
该函数把指针 ptr 所指向的空间的前 num 个字节设置为 value 值。
注意事项:
(1)确保指针 ptr 合法
(2)确保 num 为正,且在合法范围之内
(3)确保 value 的大小为 1 个字节
(4)包含头文件 string.h
使用示例:
上述代码不能使用 sizeof(s1) 替换 strlen(s1),如果替换,那么字符串 s1 末尾的空字符也会被替换为字符 ‘x’,这样打印该字符串就会越界访问。
模拟实现:
// 模拟实现库函数 memset()
void* my_memset(void* dest, int set, size_t n)
{
// 存储目标地址
void* ret = dest;
// 设置
int i;
for (i = 0; i < n; ++i)
*((char*)dest + i) = set;
// 返回
return ret;
}
四、memcmp() 函数的使用和模拟实现
函数原型:
// memcmp() 函数原型
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
函数功能:
该函数依次比较两个指针指向的空间的前 num 个字节,如果指针 ptr1 指向的内存区域大于指针 ptr2 指向的内存区域,返回一个正整数。如果指针 ptr1 指向的内存区域小于指针 ptr2 指向的内存区域,返回一个负整数。否则,返回 0。
注意事项:
(1)确保传入的两个指针合法
(2)num 需为合法正整数
(3)包含头文件 string.h
使用示例:
模拟实现:
// 模拟实现库函数 memcmp()
int my_memcmp(const void* p1, const void* p2, size_t n)
{
int i = 0;
while (i < n)
{
// 不相同
if (*((char*)p1 + i) != *((char*)p2 + i))
return *((char*)p1 + i) - *((char*)p2 + i);
else
++i;
}
// 相同
return 0;
}