一、strstr函数模拟实现
1.1 函数功能
strstr用于在字符串 str1中查找子串 str2的首次出现位置:
const char* strstr(const char* str1, const char* str2);
char* strstr(char* str1, const char* str2);
-
找到则返回第一次出现的起始地址
-
找不到则返回NULL
1.2 实现原理
场景1:简单匹配
char arr[] = "abcdefabcdef";
char* p = "cdef";
char* ret = my_strstr(arr, p); // 返回 "cdefabcdef" 的起始地址
场景2:多次匹配(复杂情况)
str1: "abbbcdef\0"
str2: "bbc\0"
可能存在多次匹配尝试,需要逐字符比较
1.3 模拟实现代码
char* my_strstr(const char* str1, const char* str2) {
assert(str1 && str2);
const char* cur = str1; // 当前查找位置
const char* s1 = NULL;
const char* s2 = NULL;
while (*cur != '\0') {
s1 = cur;
s2 = str2;
// 逐字符比较
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) {
s1++;
s2++;
}
// 如果str2完全匹配成功
if (*s2 == '\0') {
return (char*)cur; // 返回找到的位置
}
cur++; // 继续下一个位置
}
return NULL; // 未找到
}
二、memcpy函数模拟实现
2.1 函数功能
void* memcpy(void* destination, const void* source, size_t num);
-
将源内存区域的
num个字节拷贝到目标内存区域 -
返回目标空间的起始地址
-
不负责处理重叠内存的拷贝
2.2 实现原理
int arr1[] = {1,2,3,4,5,6,7,8,9,10};
int arr2[20] = {0};
my_memcpy(arr2, arr1+2, 20); // 拷贝arr1中的3,4,5,6,7
内存图示:
源地址(src): arr1+2 → 元素3的位置
目标地址(dest): arr2 → 起始位置
拷贝大小: 20字节 (5个int, 假设int为4字节)
2.3 模拟实现代码
void* my_memcpy(void* dest, const void* src, size_t num) {
void* ret = dest;
assert(dest && src);
// 逐字节拷贝
for (size_t i = 0; i < num; i++) {
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
三、memmove函数模拟实现
3.1 函数功能
void* memmove(void* destination, const void* source, size_t num);
-
与
memcpy功能类似,但能够处理内存重叠的情况 -
C语言标准规定
memcpy负责不重叠内存拷贝,memmove负责重叠内存拷贝
3.2 内存重叠问题
int arr1[] = {1,2,3,4,5,6,7,8,9,10};
my_memmove(arr1 + 2, arr1, 20); // 将前5个元素拷贝到从索引2开始的位置
拷贝前:
索引: 0 1 2 3 4 5 6 7 8 9
值: 1 2 3 4 5 6 7 8 9 10
错误拷贝方式(从前向后):
arr1[2] = arr1[0] → 1
arr1[3] = arr1[1] → 2
arr1[4] = arr1[2] → 1 (已经被覆盖!)
arr1[5] = arr1[3] → 2 (已经被覆盖!)
...
3.3 模拟实现代码(正确处理重叠)
void* my_memmove(void* dest, const void* src, size_t num) {
void* ret = dest;
assert(dest && src);
// 情况1:dest在src前面,或内存不重叠 → 从前向后拷贝
if (dest < src) {
char* d = (char*)dest;
const char* s = (const char*)src;
while (num--) {
*d++ = *s++;
}
}
// 情况2:dest在src后面,有重叠风险 → 从后向前拷贝
else {
char* d = (char*)dest + num - 1; // 指向目标末尾
const char* s = (const char*)src + num - 1; // 指向源末尾
while (num--) {
*d-- = *s--;
}
}
return ret;
}
四、关键区别与总结
4.1 strstr vs mem系列函数
| 函数 | 操作对象 | 返回类型 | 主要用途 |
|---|---|---|---|
| strstr | 字符串 | char* | 查找子串 |
| memcpy | 内存块 | void* | 非重叠内存拷贝 |
| memmove | 内存块 | void* | 任意内存拷贝(含重叠) |
4.2 memcpy vs memmove
-
标准规定:
-
memcpy:只需实现不重叠内存拷贝 -
memmove:必须处理重叠内存拷贝
-
-
实际实现:
-
某些编译器(如VS)的
memcpy也能处理重叠内存 -
但为保证可移植性,重叠时应使用
memmove
-
-
性能考虑:
-
不重叠时,两者性能相近
-
重叠时,
memmove会进行方向判断,略有开销
-
4.3 使用建议
-
查找字符串子串 → 使用
strstr -
拷贝不重叠的内存块 → 使用
memcpy(性能可能略优) -
不确定内存是否重叠 → 使用
memmove(更安全) -
需要自己实现时 → 参考上述代码,注意处理所有边界情况
五、完整测试示例
#include <stdio.h>
#include <string.h>
#include <assert.h>
// 测试代码
int main() {
// 1. 测试my_strstr
char str[] = "abcdefabcdef";
char sub[] = "cdef";
char* result = my_strstr(str, sub);
printf("strstr测试: %s\n", result ? result : "未找到");
// 2. 测试my_memcpy
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};
int arr2[20] = {0};
my_memcpy(arr2, arr1+2, 20);
printf("memcpy测试: ");
for(int i = 0; i < 5; i++) printf("%d ", arr2[i]);
printf("\n");
// 3. 测试my_memmove(重叠)
int arr3[] = {1,2,3,4,5,6,7,8,9,10};
my_memmove(arr3+2, arr3, 20);
printf("memmove测试: ");
for(int i = 0; i < 10; i++) printf("%d ", arr3[i]);
printf("\n");
return 0;
}
这些函数是C语言中处理字符串和内存的基础工具,理解它们的实现原理对于深入理解C语言内存管理和字符串操作至关重要。