0. 前言
当我们有一个字符char str1[] = "hello world!",此时,我们要新建一个字符串str2,想让str2的内容和str1一模一样,可以使用字符串函数strcpy,对两个字符串进行比较,可以使用strcmp,对字符串的操作,C语言内置的库已经给我们把解决方案都罗列好了。
但是,如果我们已经有了一个数组int arr1[],新建一个数组arr2,让arr2的内容和arr1完全一样呢,这个时候又该怎么办呢?
// 怎么让cp_arr的内容和arr完全一样?
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int cp_arr[10] = { 0 };
我们是否要这样做:
void PrintArr(int* arr, size_t num)
{
for (size_t i; i < num; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int cp_arr[20] = { 0 };
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
{
cp_arr[i] = arr[i];
}
printf("arr: ");
PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
printf("cp_arr: ");
PrintArr(arr, sizeof(cp_arr) / sizeof(cp_arr[0]));
return 0;
}
这种方法可行,但未免也太过麻烦。
C语言在标准库里早已经为我们实现好了处理这类情况的解决方案,他们都是对内存块进行操作,这类接口都是以mem开头,称为内存操作函数。
最核心最标准的内存操作函数有如下几种:
memcpy内存拷贝(不处理重叠内存,效率高)memmove内存移动(处理重叠内存,更安全)memset内存填充(批量设置字节,常用初始化 / 清零)memcmp内存比较(按字节对比两块内存)memchr内存查找(在内存块中搜索指定字节)
而memcpy就是专门解决类似刚刚那种内存拷贝的问题:
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int cp_arr[10] = { 0 };
/*for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
{
cp_arr[i] = arr[i];
}*/
memcpy(cp_arr, arr, sizeof(arr));
printf("arr: ");
PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
printf("cp_arr: ");
PrintArr(cp_arr, sizeof(cp_arr) / sizeof(cp_arr[0]));
return 0;
运行结果:
下面,我们来对这些内存操作函数进行详细了解。
1. 内存操作函数
mem开头的这些内存操作函数需要包含统一的头文件<string.h>(C++中是<cstring>),虽然他们是内存操作函数,但还是把他们归到了<string.h>中。
内存操作函数的单位一定是字节,这一点要牢记,你指定多少字节,就给你操作多少字节
1.1 memcpy

1.1.1 函数功能介绍与使用
功能:将 source 指向的内存块,直接复制 num 个字节到 destination 指向的内存块。
参数介绍:
| 参数 | 含义 |
| destination | 目标内存地址(可被转换为void*类型的指针) |
| source | 源数据内存地址(可被转换为const void*类型的指针) |
| num | 要复制的字节数,类型为无符号整数size_t |
使用:
先定义一个测试用的结构体:
// 定义一个测试用结构体
struct Student {
int id;
int age;
char name[20];
};
可以拷贝int数组:
// ==========================
// 1. memcpy 拷贝 int 数组
// ==========================
int arr1[5] = { 10, 20, 30, 40, 50 };
int arr2[5] = { 0 }; // 初始化为0
// 拷贝 5个int 的内存
memcpy(arr2, arr1, 5 * sizeof(int));
printf("===== int数组拷贝 =====\n");
for (int i = 0; i < 5; i++) {
printf("%d ", arr2[i]);
}
printf("\n\n");

也可以拷贝char数组,拷贝char数组要注意一个问题,他一定是按照你指定的字节数去拷贝的,不会给你加上\0。
// 3. memcpy 拷贝 char数组
// 【重点:不处理 \0,只按字节拷贝】
// ==========================
char str1[20] = "hello world"; // 自带 \0
char str2[20] = "---------------"; // 用来观察覆盖效果
printf("===== char数组拷贝(重点:\\0问题) =====\n");
printf("拷贝前 str2 = %s\n", str2);
// 只拷贝 5 个字节:'h','e','l','l','o'
// 注意:没有拷贝字符串末尾的 \0 !!!
memcpy(str2, str1, 5 * sizeof(char));
printf("拷贝后 str2 = %s\n", str2);
printf("原因:memcpy 只拷贝 5 个字节,不会自动加 \\0,后面的内容保留!\n");

还可以拷贝结构体:
// ==========================
// 2. memcpy 拷贝 结构体
// ==========================
struct Student stu1 = { 1001, 20, "ZhangSan" };
struct Student stu2;
// 直接拷贝整个结构体的内存
memcpy(&stu2, &stu1, sizeof(struct Student));
printf("===== 结构体拷贝 =====\n");
printf("学号:%d\n年龄:%d\n姓名:%s\n\n", stu2.id, stu2.age, stu2.name);

由此可见,memcpy的操作他是这样的,图示:

1.1.2 memcpy的模拟实现
如果这个函数功能要由我们自己来实现呢,对一个接口进行模拟实现,首先看的就是他的参数和返回值,参数这里不再赘述,memcpy的返回值是:

也就是目标内存块的起始地址
对memcpy的模拟实现,他既然要操作num个字节,要想一个字节一个字节的操作,我们肯定是要借助char*,实现如下:
void* my_memcpy(void* des, const void* src, size_t num)
{
// 你给我传进来的两个指针不能为空
assert(des && src);
// 一个字节一个字节拷贝,总共有num个字节
for (size_t i = 0; i < num; ++i)
((char*)des)[i] = ((char*)src)[i];
// 返回值要是目标内存块的起始地址
return des;
}
这个函数的模拟实现整体还是非常简单的,测试一下,将刚刚的测试用例全部换成我们自己实现的memcpy:
// ==========================
// 1. memcpy 拷贝 int 数组
// ==========================
int arr1[5] = { 10, 20, 30, 40, 50 };
int arr2[5] = { 0 }; // 初始化为0
// 拷贝 5个int 的内存
my_memcpy(arr2, arr1, 5 * sizeof(int));
printf("===== int数组拷贝 =====\n");
for (int i = 0; i < 5; i++) {
printf("%d ", arr2[i]);
}
printf("\n\n");
// ==========================
// 2. memcpy 拷贝 结构体
// ==========================
struct Student stu1 = { 1001, 20, "ZhangSan" };
struct Student stu2;
// 直接拷贝整个结构体的内存
my_memcpy(&stu2, &stu1, sizeof(struct Student));
printf("===== 结构体拷贝 =====\n");
printf("学号:%d\n年龄:%d\n姓名:%s\n\n", stu2.id, stu2.age, stu2.name);
//==========================
//3. memcpy 拷贝 char数组
//【重点:不处理 \0,只按字节拷贝】
//==========================
char str1[20] = "hello world"; // 自带 \0
char str2[20] = "---------------"; // 用来观察覆盖效果
printf("===== char数组拷贝(重点:\\0问题) =====\n");
printf("拷贝前 str2 = %s\n", str2);
// 只拷贝 5 个字节:'h','e','l','l','o'
// 注意:没有拷贝字符串末尾的 \0 !!!
my_memcpy(str2, str1, 5 * sizeof(char));
printf("拷贝后 str2 = %s\n", str2);
printf("原因:memcpy 只拷贝 5 个字节,不会自动加 \\0,后面的内容保留!\n");

结果非常完美,没有任何问题。
但是但是,如果说我们要做的操作是这样呢?

我想做的是一个内存块内部的拷贝,那我们刚刚实现的memcpy还能解决这个问题吗?
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr + 2, arr, sizeof(int) * 5);
return 0;
}

很显然并不能,因为把每次拷贝的细节掰开来看的话,当我们将1拷贝到3, 2拷贝到4的位置的时候,原本的值就被覆盖了,等再想把3拷贝到5这个位置的时候,3已经不见了,取而代之的刚刚拷贝过来的1,于是就会出现这样的情况。
在这种情况下,我们就引出了下面这个内存操作函数-----memmove
1.2 memmove

1.2.1 函数功能介绍与使用
返回值:返回目标内存块的起始地址。
功能:从 source 拷贝 num 个字节到 destination,支持处理内存重叠场景。
他的参数和memcpy一模一样,都是对内存块的操作,唯一的不同是:memmove 是一个支持内存重叠的安全版 memcpy,拷贝逻辑更鲁棒,代价是比 memcpy 略慢(多了方向判断和分支)。
函数的使用:
1. 将刚刚的情况换成memmove,还会出现刚刚的问题吗?
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, sizeof(int) * 5);
PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
return 0;
}
答案是当然不会
2. 再看一段测试用例:
int main()
{
// 测试用数组(内存重叠场景)
char arr[] = "abcdefghijklmn";
printf("原字符串:%s\n", arr);
// 核心:从 arr+2 拷贝 5 个字符到 arr 位置
// 源和目标内存重叠,必须用 memmove!
memmove(arr, arr + 2, 5);
printf("移动后:%s\n", arr);
return 0;
}

memmove = 能安全处理内存重叠的 memcpy这就是它唯一、也是最重要的功能!
那这个函数,怎么进行模拟实现呢?
1.2.2 memmove的模拟实现
memmove的模拟实现,牢记一个点,他的实现就会变得非常简单。在一个内存块上对数据的操作,势必会造成覆盖这种问题,从而导致结果出错,当你就是要做重叠操作的时候,覆盖是一定会发生的,我们可以发送覆盖,但要切记:
一定不要覆盖,你将来会使用的数据。将来这个词很关键,也就是从侧面说明,你可以覆盖你已经用过的数据,这样结果就不会出问题了。
所以刚刚那个例子,我们可以这样去操作:

从后往前拷贝,这样不就不会被覆盖了吗。
可是,一味的从后往前拷贝,就不会出问题吗?答案是否定的,一定会出问题,看下面这种场景:

我想把后面的3,4,5,6,7,挪到数组最开始的位置,变成3,4,5,6,7,5,6,8,9,10。
从后往前挪?能做到吗?
这种情况是做不到的,因为在7挪到5的位置的时候,5还没有被使用,这就造成了破坏性的覆盖,6的道理也是一样的。所以这种情况下从后往前挪就不行。
因此,在这种情况下,需要从前往后挪,先挪3,7最后挪,中间当5把3覆盖的时候,3已经被使用,不怕覆盖,6覆盖4,7覆盖5也是同样的道理。
经过上述分析,memmove的模拟实现的总体思路就已经全部出来了,非常直观:
当dst在src之前的时候,我们从前往后拷贝,不怕覆盖,当dst在src之后的时候,我们从后往前拷贝,也不怕覆盖:
void* my_memmove(void* des, const void* src, size_t num)
{
assert(des && src);
// memmove的模拟实现,主要就是抓住一个点:你不要覆盖自己将来可能会用到的数据
// 当des再src之前,正常从前往后拷贝
if ((char*)des < (char*)src)
{
for (size_t i = 0; i < num; ++i)
((char*)des)[i] = ((char*)src)[i];
}
else
{
// 这个地方的i一定不能用size_t类型,要不然i>=0的边界是一定会出问题的,会死循环
for (int i = num - 1; i >= 0; --i)
((char*)des)[i] = ((char*)src)[i];
}
return des;
}
测试一下:
int main()
{
// 测试用数组(内存重叠场景)
char arr1[] = "abcdefghijklmn";
printf("原字符串:%s\n", arr1);
// 核心:从 arr+2 拷贝 5 个字符到 arr 位置
// 源和目标内存重叠,必须用 memmove!
my_memmove(arr1, arr1 + 2, 5);
printf("移动后:%s\n", arr1);
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, sizeof(int) * 5);
PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
return 0;
}

没有任何问题,完美实现
从模拟实现也可以看出来,就算内存没有重叠,memmove也完全可用,他比memcpy更安全,就是会稍微慢一点,毕竟安全的代价当然就是实现起来更复杂一点点。
1.3 一种现象
看这段代码:
int main()
{
// 同一数组,内存高度重叠
char arr[30] = "abcdefghijklmn";
printf("修改前:%s\n", arr);
// 危险操作:目标地址 < 源地址,内存重叠!
// 标准说 memcpy 行为未定义
memcpy(arr, arr + 2, 5);
printf("修改后:%s\n", arr);
return 0;
}

这是C标准库里实现的memcpy,可以很明显的看到,memcpy对重叠内存块的操作,也是没有任何问题的,由此我们可以大胆推测:
C标准库的memcpy的实现,是采用的memmove的实现方式,他给你把重叠问题也给处理了。
这种实现是没有任何问题的,memcpy只需要把内存块不重叠的情况解决就可以了,但是他帮你把重叠的也给解决了。就是说老师只需要你考60分,但是你考了100分,行还不是不行?太行了!所以这一点大家也要注意
剩下的内存操作函数,memset,cmp,chr就很简单了,功能,作用都很单一。
1.4 memset

返回值:返回内存块的起始地址 ptr。
核心功能:将 ptr 指向的内存块的前 num 个字节,全部设置为 value 对应的字节值(按 unsigned char 解释)。
逐字节填充:它是按字节操作的,value 最终会被截断为 unsigned char 类型,只取低 8 位。比如 memset(arr, 0x1234, 4),实际填充的是 0x34 这个字节。这是历史的产物,也可以说是C语言的一个小缺陷。
无视类型:不管 ptr 指向的是 char[]、int[] 还是结构体,都按字节填充,因此常用来初始化 / 清零内存。
无终止符检查:和 memcpy 一样,只看 num 字节,不管 \0。
典型使用场景:
int main()
{
int arr[5];
// 1. 数组清零(最常用)
memset(arr, 0, sizeof(arr));
char str[10];
// 2. 填充指定字符
memset(str, '#', sizeof(str)-1);
str[9] = '\0'; // 手动补结束符
printf("str = %s\n", str);
return 0;
}

模拟实现:
void* my_memset(void* ptr, int value, size_t num)
{
assert(ptr);
unsigned char* p = (unsigned char*)ptr;
unsigned char val = (unsigned char)value;
for (size_t i = 0; i < num; ++i)
{
p[i] = val;
}
return ptr;
}
正确用法:只用来清零或填充单字节值。
1.5 memcmp
功能 :比较 ptr1 和 ptr2 指向的两块内存的前 num 个字节,按无符号字符逐字节对比,返回表示大小关系的结果。
关键特性:
不终止于 \0:与 strcmp 最大的区别是,它不会因为遇到 \0 就停止比较,会严格对比指定的 num 个字节。
按字节无符号比较:比较时按 unsigned char 解释每个字节的值,因此 0xFF 会被认为大于 0x00。
返回值含义:
| 返回值 | 含义 |
|---|---|
< 0 | ptr1 中第一个不匹配的字节值小于 ptr2 中的对应字节 |
= 0 | 两块内存的前 num 个字节完全相同 |
> 0 | ptr1 中第一个不匹配的字节值大于 ptr2 中的对应字节 |
使用场景:
int main()
{
char str1[] = "abc\0def";
char str2[] = "abc\0xyz";
// 对比前 5 个字节,不受中间 \0 影响
int ret = memcmp(str1, str2, 5);
if (ret == 0)
printf("两块内存前5个字节相同\n");
else if (ret < 0)
printf("str1 小于 str2\n");
else
printf("str1 大于 str2\n");
return 0;
}

模拟实现:
int my_memcmp(const void *ptr1, const void *ptr2, size_t num)
{
const unsigned char *p1 = (const unsigned char *)ptr1;
const unsigned char *p2 = (const unsigned char *)ptr2;
for (size_t i = 0; i < num; ++i)
{
if (p1[i] != p2[i])
{
return p1[i] - p2[i];
}
}
return 0;
}
注意!!!
不要用 memcmp 比较包含指针、浮点数等复杂类型的结构体:这些类型的字节表示可能有填充、对齐或实现差异,即使逻辑上相等,字节对比也可能失败。
只适合比较原始字节数据(如字节数组、字符串内容)。
1.6 memchr
功能:在 ptr 指向的内存块的前 num 个字节中,按无符号字节查找第一个与 value 匹配的字节,找到则返回该字节的地址,找不到返回 NULL。
关键特性
逐字节无符号比较:value 会被截断为 unsigned char 再参与比较,因此 memchr(arr, 0x1234, ...) 实际查找的是 0x34。
无视 \0:和 strchr 最大的区别是,它不会因为遇到字符串结束符 \0 就停止,会严格搜索指定的 num 个字节。
找到即返回:只返回第一个匹配项的地址。
使用场景:
int main()
{
char str[] = "abc\0defghij";
// 在前 8 个字节中查找字符 'd'
char *p = (char*)memchr(str, 'd', 8);
if (p != NULL)
printf("找到了:%s\n", p);
else
printf("没找到\n");
return 0;
}

模拟实现:
void* my_memchr(const void *ptr, int value, size_t num)
{
const unsigned char *p = (const unsigned char*)ptr;
unsigned char val = (unsigned char)value;
for (size_t i = 0; i < num; ++i)
{
if (p[i] == val)
{
return (void*)(p + i);
}
}
return NULL;
}
2. 总结
| 函数 | 核心功能 | 关键特性 |
|---|---|---|
memcpy | 内存拷贝 | 不处理重叠,效率高 |
memmove | 内存移动 | 支持重叠拷贝,更安全 |
memset | 内存填充 | 按字节设置值,常用于清零 |
memcmp | 内存比较 | 逐字节对比,无视 \0 |
memchr | 内存查找 | 按字节搜索,无视 \0 |
这五个函数的本质都是按字节操作内存,区别只在于:
拷贝 / 移动:处理数据位置
填充:批量设置字节值
比较 / 查找:逐字节对比和搜索
它们都无视数据类型,也不关心字符串结束符 \0,这是和字符串函数最核心的区别。
附录:完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//int main()
//{
// char str1[] = "hello world!";
// char str2[20];
// printf("str1 = %s\n", str1);
// strcpy(str2, str1);
// printf("str2 = %s\n", str1);
//
// return 0;
//}
void PrintArr(int* arr, size_t num)
{
for (size_t i = 0; i < num; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//int main()
//{
//
//
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int cp_arr[10] = { 0 };
//
// /*for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
// {
// cp_arr[i] = arr[i];
// }*/
//
// memcpy(cp_arr, arr, sizeof(arr));
//
// printf("arr: ");
// PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
//
// printf("cp_arr: ");
// PrintArr(cp_arr, sizeof(cp_arr) / sizeof(cp_arr[0]));
//
// return 0;
//}
#include <assert.h>
void* my_memcpy(void* des, const void* src, size_t num)
{
// 你给我传进来的两个指针不能为空
assert(des && src);
// 一个字节一个字节拷贝,总共有num个字节
for (size_t i = 0; i < num; ++i)
((char*)des)[i] = ((char*)src)[i];
// 返回值要是目标内存块的起始地址
return des;
}
//// 定义一个测试用结构体
//struct Student {
// int id;
// int age;
// char name[20];
//};
//
//int main() {
// // ==========================
// // 1. memcpy 拷贝 int 数组
// // ==========================
// int arr1[5] = { 10, 20, 30, 40, 50 };
// int arr2[5] = { 0 }; // 初始化为0
//
// // 拷贝 5个int 的内存
// my_memcpy(arr2, arr1, 5 * sizeof(int));
//
// printf("===== int数组拷贝 =====\n");
// for (int i = 0; i < 5; i++) {
// printf("%d ", arr2[i]);
// }
// printf("\n\n");
//
//
// // ==========================
// // 2. memcpy 拷贝 结构体
// // ==========================
// struct Student stu1 = { 1001, 20, "ZhangSan" };
// struct Student stu2;
//
// // 直接拷贝整个结构体的内存
// my_memcpy(&stu2, &stu1, sizeof(struct Student));
//
// printf("===== 结构体拷贝 =====\n");
// printf("学号:%d\n年龄:%d\n姓名:%s\n\n", stu2.id, stu2.age, stu2.name);
//
//
// //==========================
// //3. memcpy 拷贝 char数组
// //【重点:不处理 \0,只按字节拷贝】
// //==========================
// char str1[20] = "hello world"; // 自带 \0
// char str2[20] = "---------------"; // 用来观察覆盖效果
//
// printf("===== char数组拷贝(重点:\\0问题) =====\n");
// printf("拷贝前 str2 = %s\n", str2);
//
// // 只拷贝 5 个字节:'h','e','l','l','o'
// // 注意:没有拷贝字符串末尾的 \0 !!!
// my_memcpy(str2, str1, 5 * sizeof(char));
//
// printf("拷贝后 str2 = %s\n", str2);
// printf("原因:memcpy 只拷贝 5 个字节,不会自动加 \\0,后面的内容保留!\n");
//
// return 0;
//}
//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// my_memcpy(arr + 2, arr, sizeof(int) * 5);
// PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
//
// return 0;
//}
//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// memmove(arr + 2, arr, sizeof(int) * 5);
// PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
//
// return 0;
//}
void* my_memmove(void* des, const void* src, size_t num)
{
assert(des && src);
// memmove的模拟实现,主要就是抓住一个点:你不要覆盖自己将来可能会用到的数据
// 当des再src之前,正常从前往后拷贝
if ((char*)des < (char*)src)
{
for (size_t i = 0; i < num; ++i)
((char*)des)[i] = ((char*)src)[i];
}
else
{
// 这个地方的i一定不能用size_t类型,要不然i>=0的边界是一定会出问题的,会死循环
for (int i = num - 1; i >= 0; --i)
((char*)des)[i] = ((char*)src)[i];
}
return des;
}
//int main()
//{
// // 测试用数组(内存重叠场景)
// char arr1[] = "abcdefghijklmn";
//
// printf("原字符串:%s\n", arr1);
//
// // 核心:从 arr+2 拷贝 5 个字符到 arr 位置
// // 源和目标内存重叠,必须用 memmove!
// my_memmove(arr1, arr1 + 2, 5);
//
// printf("移动后:%s\n", arr1);
//
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// my_memmove(arr + 2, arr, sizeof(int) * 5);
// PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
//
// return 0;
//}
//int main()
//{
// // 同一数组,内存高度重叠
// char arr[30] = "abcdefghijklmn";
//
// printf("修改前:%s\n", arr);
//
// // 危险操作:目标地址 < 源地址,内存重叠!
// // 标准说 memcpy 行为未定义
// memcpy(arr, arr + 2, 5);
//
// printf("修改后:%s\n", arr);
//
// return 0;
//}
//int main()
//{
// int arr[5];
// // 1. 数组清零(最常用)
// memset(arr, 0, sizeof(arr));
//
// char str[10];
// // 2. 填充指定字符
// memset(str, '#', sizeof(str) - 1);
// str[9] = '\0'; // 手动补结束符
//
// printf("str = %s\n", str);
// return 0;
//}
//void* my_memset(void* ptr, int value, size_t num)
//{
// assert(ptr);
// unsigned char* p = (unsigned char*)ptr;
// unsigned char val = (unsigned char)value;
//
// for (size_t i = 0; i < num; ++i)
// {
// p[i] = val;
// }
// return ptr;
//}
//int main()
//{
// char str1[] = "abc\0def";
// char str2[] = "abc\0xyz";
//
// // 对比前 5 个字节,不受中间 \0 影响
// int ret = memcmp(str1, str2, 5);
//
// if (ret == 0)
// printf("两块内存前5个字节相同\n");
// else if (ret < 0)
// printf("str1 小于 str2\n");
// else
// printf("str1 大于 str2\n");
//
// return 0;
//}
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
const unsigned char* p1 = (const unsigned char*)ptr1;
const unsigned char* p2 = (const unsigned char*)ptr2;
for (size_t i = 0; i < num; ++i)
{
if (p1[i] != p2[i])
{
return p1[i] - p2[i];
}
}
return 0;
}
int main()
{
char str[] = "abc\0defghij";
// 在前 8 个字节中查找字符 'd'
char* p = (char*)memchr(str, 'd', 8);
if (p != NULL)
printf("找到了:%s\n", p);
else
printf("没找到\n");
return 0;
}
void* my_memchr(const void* ptr, int value, size_t num)
{
const unsigned char* p = (const unsigned char*)ptr;
unsigned char val = (unsigned char)value;
for (size_t i = 0; i < num; ++i)
{
if (p[i] == val)
{
return (void*)(p + i);
}
}
return NULL;
}

746

被折叠的 条评论
为什么被折叠?



