目录
[1. memcpy使用和模拟实现](#1. memcpy使用和模拟实现)
[1.1 函数功能介绍](#1.1 函数功能介绍)
[1.3 模拟实现](#1.3 模拟实现)
[2. memmove使用和模拟实现](#2. memmove使用和模拟实现)
[2.1 函数功能介绍](#2.1 函数功能介绍)
[2.2 函数使用示例](#2.2 函数使用示例)
[2.3 模拟实现](#2.3 模拟实现)
[3. memset函数的使用](#3. memset函数的使用)
[3.1 函数功能介绍](#3.1 函数功能介绍)
[4. memcmp函数的使用](#4. memcmp函数的使用)
[4.1 函数功能介绍](#4.1 函数功能介绍)
1. memcpy使用和模拟实现
1.1 函数功能介绍
memcpy 函数用于从源内存地址的起始位置开始 ,复制指定字节数的数据到目标内存地址的起始位置 。它与 strcpy 的核心区别在于:strcpy 仅用于字符串复制,会以 '\0' 作为结束标志,而 memcpy 可用于复制任意类型的数据( 如整型、字符型、结构体等),复制的长度由第三个参数明确指定,不依赖结束标志。
函数原型:void memcpy(void destination, const void* source, size_t num)**;
参数说明:
-
destination:目标内存地址,接收复制后的数据,注意该地址需可修改(不能是常量地址)。
-
source:源内存地址,提供需要复制的数据,用 const 修饰,确保源数据不被修改。
-
num:需要复制的字节数(size_t 是无符号整型类型)。
返回值:返回目标内存地址 destination(便于链式调用)。
注意事项:memcpy 函数默认不处理源内存块和目标内存块重叠的情况(若出现重叠,复制结果未定义)。
实例:
cpp
#include <stdio.h>
#include <string.h> // memcpy 所在头文件
int main() {
// 示例1:复制字符数组
char src1[] = "hello world";
char dest1[20] = {0};
memcpy(dest1, src1, strlen(src1) + 1); // +1 是为了复制字符串结束标志 '\0'
printf("复制字符数组结果:%s\n", dest1); // 输出:hello world
// 示例2:复制整型数组
int src2[] = {1, 2, 3, 4, 5};
int dest2[5] = {0};
memcpy(dest2, src2, sizeof(src2)); // sizeof(src2) 计算整个整型数组的字节数
for (int i = 0; i < 5; i++) {
printf("%d ", dest2[i]); // 输出:1 2 3 4 5
}
printf("\n");
return 0;
}

1.3 模拟实现
模拟实现思路:
-
参数合法性检查:确保 destination 和 source 不为 NULL(空指针),否则直接返回 NULL。
-
类型转换:由于函数参数是void* 类型(通用指针) ,无法直接解引用操作,需先将其转换为 char* 类型 (按字节操作,适配任意数据类型)。
-
循环复制:通过循环逐字节复制数据,循环次数为 num(复制的字节数),每复制一个字节后,两个指针均向后移动一位。
-
返回目标地址:复制完成后,返回 destination 地址。
cpp
#include<stdio.h>
#include<string.h>
#include<assert.h>
void *my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
((char*)src)++;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = {0};
//memcpy--对源数组和目标数组之间的数据进行复制,从源数组的起始位置开始,
// 复制n个字节到目标数组的起始位置,如果源数组和目标数组有重叠的部分,则覆盖目标数组的部分内存
my_memcpy(arr2, arr1, 20);
for (int i = 0; i < 20; i++) {
printf("%d ", arr2[i]);
}
printf("\n");
char arr3[20] = "hello,world";
char arr4[40] = { 0 };
void*p=my_memcpy(arr4, arr3, strlen(arr3) + 1); //+1 是为了复制字符串结束标志 '\0'
printf("%s",(char*)p);
}

2. memmove使用和模拟实现
2.1 函数功能介绍
memmove 函数的功能与 memcpy 基本一致,均用于从源内存地址复制 num 个字节到目标内存地址。核心区别在于:memmove 支持源内存块和目标内存块重叠的情况,能够正确处理重叠区域的复制。
函数原型:void* memmove(void* destination, const void* source, size_t num);
参数说明:与 memcpy 完全相同(destination:目标地址、source:源地址、num:复制字节数)。
返回值:返回目标内存地址 destination。
内存重叠场景说明:当 source 地址范围与 destination 地址范围有重叠时,例如:char arr[] = "123456789"; 要将 arr[2] 开始的 5 个字节复制到 arr[0] 开始的位置,此时源区域和目标区域重叠,使用 memcpy 可能导致复制结果错误,而 memmove 可正确处理。
2.2 函数使用示例
cpp
#include <stdio.h>
#include <string.h>
int main() {
// 测试内存重叠场景
char arr[] = "123456789";
printf("复制前:%s\n", arr); // 输出:123456789
// 将 arr[2] 开始的 5 个字节("34567")复制到 arr[0] 开始的位置
memmove(arr, arr + 2, 5);
printf("复制后:%s\n", arr); // 输出:345676789
// 测试非重叠场景(与 memcpy 效果一致)
int src[] = { 1, 2, 3, 4, 5 };
int dest[5] = { 0 };
memmove(dest, src, sizeof(src));
for (int i = 0; i < 5; i++) {
printf("%d ", dest[i]); // 输出:1 2 3 4 5
}
printf("\n");
return 0;
}

2.3 模拟实现
模拟实现思路:
-
参数合法性检查:通过 assert 确保 dest 和 src 不为空指针。
-
处理内存重叠:分两种情况判断:
-
当目标地址 dest 在源地址 src 之前(dest < src):采用正向复制(从前往后逐字节复制),避免覆盖未复制的源数据。
-
当目标地址 dest 在源地址 src 之后(dest > src):采用反向复制(从后往前逐字节复制),同样避免覆盖未复制的源数据。
-
-
类型转换与复制:将 void* 转换为 char* 按字节操作,复制完成后返回 dest 地址。
cpp
#include <stdio.h>
#include <string.h>
#include<assert.h>
// 修复1:const* src 语法错误 → const void* src
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
((char*)src)++;
}
}
else
{
while (num--)
{
// 修复2:src侧少写取值符* → *((char*)src + num)
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main() {
char arr[] = "123456789";
printf("复制前:%s\n", arr); // 输出:123456789
// dest>src(arr+2 > arr),走else分支从后往前拷贝
my_memmove(arr + 2, arr, 5);
// 实际正确输出:1212345789(而非你预期的345676789,原因见下文)
printf("复制后:%s\n", arr);
int src[] = { 1, 2, 3, 4, 5 ,7,8,9,10 };
// 修复3:5*sizeof(int) 是正确的(仅拷贝5个int),保留你的写法
my_memmove(src, src + 2, 5 * sizeof(int));
// 修复4:循环i<10越界 → i<9(数组只有9个元素)
for (int i = 0; i < 9; i++) {
printf("%d ", src[i]); // 输出:3 4 5 7 8 7 8 9 10
}
printf("\n");
return 0;
}

3. memset函数的使用
3.1 函数功能介绍
memset 函数用于将指定内存块的前 num 个字节设置为特定的字符 (注意:是按字节设置,不是按数据类型设置)。它常被用于初始化内存块,例如将数组初始化为 0、将字符串初始化为特定字符等。
函数原型:void* memset(void* ptr, int value, size_t num);
参数说明:
-
ptr:需要设置的内存块起始地址(可修改)。
-
value:需要设置的字符值(虽然参数类型是 int,但实际只使用低 8 位,即 ASCII 码范围)。
-
num:需要设置的字节数。
返回值:返回设置后的内存块起始地址 ptr。
注意事项:memset 是按字节设置的,因此不能直接用于设置非字符类型的数组为任意值 (例如:不能用 memset 将 int 数组设置为 1,因为每个 int 占 4 字节,会被设置为 0x01010101,即十进制的 16843009)。
cpp
#include <stdio.h>
#include <string.h>
#define _CRT_SECURE_NO_WARNINGS
int main() {
// 示例1:初始化字符数组
char arr1[20] = { 0 };
memset(arr1, 'a', 5); // 将前 5 个字节设置为 'a'
printf("字符数组初始化结果:%s\n", arr1); // 输出:aaaaa(后面为 '\0',不显示)
// 示例2:清空字符数组(设置为 '\0')
char arr2[] = "hello memset";
memset(arr2, '\0', strlen(arr2)); // 将整个字符串设置为 '\0'
printf("清空后字符数组:%s\n", arr2); // 输出空(无内容)
// 示例3:初始化整型数组为 0(可行,因为 0 的 ASCII 码对应字节 0x00)
int arr3[5] = { 1, 2, 3, 4, 5 };
memset(arr3, 0, sizeof(arr3)); // 每个字节都设为 0,整个 int 就是 0
for (int i = 0; i < 5; i++) {
printf("%d ", arr3[i]); // 输出:0 0 0 0 0
}
printf("\n");
// 错误示例:试图将 int 数组设置为 1(结果不符合预期)
int arr4[3] = { 0 };
memset(arr4, 1, sizeof(arr4)); // 每个字节设为 1,int 为 0x01010101 = 16843009
for (int i = 0; i < 3; i++) {
printf("%d ", arr4[i]); // 输出:16843009 16843009 16843009
}
printf("\n");
return 0;
}

4. memcmp函数的使用
4.1 函数功能介绍
memcmp 函数用于比较两个内存块的前 num 个字节内容是否相 同。它与 strcmp 的区别在于:strcmp 仅比较字符串(以 '\0' 结束),而 memcmp 可比较任意类型的内存块 (按字节逐一比较,不依赖结束标志)。
函数原型:int memcmp(const void* ptr1, const void* ptr2, size_t num);
参数说明:
-
ptr1:第一个内存块的起始地址。
-
ptr2:第二个内存块的起始地址。
-
num:需要比较的字节数。
返回值(比较规则:按字节的 ASCII 码值大小比较,逐字节对比,直到找到不同字节或比较完 num 个字节):
-
返回值 < 0:ptr1 指向的内存块中第一个不同字节的 ASCII 码值小于 ptr2 对应的字节。
-
返回值 = 0:两个内存块的前 num 个字节完全相同。
-
返回值 > 0:ptr1 指向的内存块中第一个不同字节的 ASCII 码值大于 ptr2 对应的字节。
cpp
#include <stdio.h>
#include <string.h>
int main() {
// 示例1:比较字符数组(与 strcmp 效果一致,当比较长度覆盖整个字符串时)
char arr1[] = "hello";
char arr2[] = "helloworld";
int ret1 = memcmp(arr1, arr2, 5); // 比较前 5 个字节
printf("字符数组比较结果(前5字节):%d\n", ret1); // 输出:0(前5字节均为 "hello")
int ret2 = memcmp(arr1, arr2, 6); // 比较前 6 个字节(arr1[5] 是 '\0',arr2[5] 是 'w')
printf("字符数组比较结果(前6字节):%d\n", ret2); // 输出:-1('\0' ASCII 码小于 'w')
// 示例2:比较整型数组
int arr3[] = {1, 2, 3, 4}; // 十六进制:0x01 0x00 0x00 00, 0x02 0x00 0x00 00...
int arr4[] = {1, 2, 4, 5};
int ret3 = memcmp(arr3, arr4, 8); // 比较前 8 字节(即前 2 个 int,每个 int 4 字节)
printf("整型数组比较结果(前2个元素):%d\n", ret3); // 输出:0(前 2 个元素相同)
int ret4 = memcmp(arr3, arr4, 12); // 比较前 12 字节(前 3 个 int)
printf("整型数组比较结果(前3个元素):%d\n", ret4); // 输出:-1(第3个元素 3 < 4,按字节比较时 0x03 < 0x04)
return 0;
}
