收集飞花令碎片——C语言内存函数
“山在雾中若隐若现,水在光里缓缓流淌。
我撑一叶小舟,不问归期,
只想听这天地的一声叹息。”
在 C语言 中,所谓的内存函数
(Memory Functions)
,是指用于直接操作内存内容的一组标准库函数,主要定义在头文件:#include <string.h>
👉这些函数通常以
mem
开头,主要作用是:
👉在内存中复制、比较、设置或移动一段字节数据
👉它们操作的单位是字节(byte)
,而不是字符或字符串
各函数详解
- 1️⃣ memcpy(void *dest, const void *src, size_t n)
- 2️⃣ memmove(void *dest, const void *src, size_t n)
- 3️⃣ memset(void *ptr, int value, size_t n)
- 4️⃣ memcmp(const void *a, const void *b, size_t n)
- 5️⃣ memchr(const void *ptr, int value, size_t n)
- 两内存函数的模拟实现
- memcpy
- 为何memcpy不处理内存重叠情况
- memmove
- 如果你觉得对你有帮助
- 请给我一个三连哦谢谢啦
1️⃣ memcpy(void *dest, const void *src, size_t n)
- 作用: 从源内存区域复制
n
个字节到目标区域。 - 注意: 源和目标区域不能重叠!
char src[] = "Hello";
char dest[10];
memcpy(dest, src, 6); // 复制包括'\0'在内的6个字节
printf("%s\n", dest); // 输出 Hello
空间卷轴:memcpy函数
2️⃣ memmove(void *dest, const void *src, size_t n)
- 作用: 与 memcpy 类似,但支持重叠区域。
char data[] = "12345";
memmove(data + 2, data, 3); // 支持重叠
printf("%s\n", data); // 输出 12123
空间卷轴:memmove函数
3️⃣ memset(void *ptr, int value, size_t n)
- 作用: 将从 ptr 开始的 n 个字节都设为 value(按字节存)。
char buf[10];
memset(buf, 'A', 5);
buf[5] = '\0';
printf("%s\n", buf); // 输出 AAAAA
www空间卷轴:memset
4️⃣ memcmp(const void *a, const void *b, size_t n)
-
作用: 比较两块内存前 n 个字节。
-
返回值:
-
< 0
:a < b
-
= 0
:a == b
-
> 0
:a > b
char a[] = "abc";
char b[] = "abd";
int result = memcmp(a, b, 3);
printf("%d\n", result); // 输出负数
空间卷轴:memcmp函数
5️⃣ memchr(const void *ptr, int value, size_t n)
- 作用: 在内存中查找值为 value 的字节,返回首次出现位置的指针。
char str[] = "OpenAI";
char *p = memchr(str, 'A', 6);
if (p)printf("Found at: %s\n", p); // 输出 AI
空间卷轴:memchr函数
两内存函数的模拟实现
memcpy
/*** 内存复制函数* 将指定字节数的数据从源内存地址复制到目标内存地址* * @param dst 目标内存地址(复制目的地)* @param src 源内存地址(复制来源) * @param count 要复制的字节数* @return 返回目标内存地址的原指针*/
void *memcpy(void *dst, const void *src, size_t count)
{// 保存原始目标指针,用于函数返回void *ret = dst;// 参数有效性检查:确保目标地址和源地址都不为空assert(dst != NULL);assert(src != NULL);/** 从低地址到高地址逐字节复制* 注意:此实现不处理内存重叠情况*/while (count--) {// 逐字节复制:将源地址的一个字节复制到目标地址*(char *)dst = *(char *)src;// 移动目标指针到下一个字节位置dst = (char *)dst + 1;// 移动源指针到下一个字节位置 src = (char *)src + 1;}// 返回原始的目标指针(标准memcpy函数的约定)return ret;
}
为何memcpy不处理内存重叠情况
memcpy(data + 2, data, 5);
// 源: data[0] 开始的5个字节 "abcde"
// 目标: data[2] 开始的5个字节
初始: a b c d e f g h i \0
步骤: a b a d e f g h i \0 (复制data[0]到data[2])a b a b e f g h i \0 (复制data[1]到data[3]) a b a b a f g h i \0 (复制data[2]到data[4])a b a b a b g h i \0 (复制data[3]到data[5])a b a b a b a h i \0 (复制data[4]到data[6])
结果: "abababa hi"
- 关键问题:
第3步:data[4]
=data[2]
,但此时data[2]
已经被改为a
(不是原来的c
)
源数据在复制过程中被修改了!
// 循环展开 - 逐字节复制
*(data+2) = *(data+0); // data[2] = 'a' → "abade fghi"
*(data+3) = *(data+1); // data[3] = 'b' → "ababe fghi"
*(data+4) = *(data+2); // data[4] = 'a' → "ababa fghi" ← 问题出现!
*(data+5) = *(data+3); // data[5] = 'b' → "abababghi"
*(data+6) = *(data+4); // data[6] = 'a' → "abababa hi"
memmove
/*** 安全的内存移动函数* 处理内存重叠情况,保证复制结果的正确性* * @param dst 目标内存地址* @param src 源内存地址 * @param count 要复制的字节数* @return 返回目标内存地址*/
void *memmove(void *dst, const void *src, size_t count)
{// 保存原始目标指针用于返回void *ret = dst;// 检查是否不需要重叠处理的情况:// 1. 目标地址 <= 源地址 (反向重叠或不重叠)// 2. 目标地址 >= 源地址 + count (完全不重叠)if (dst <= src || (char *)dst >= ((char *)src + count)) {/** 非重叠缓冲区 或 反向重叠* 从低地址到高地址复制(正向复制)* * 内存布局示例:* 情况1: dst <= src (反向重叠)* [dst...dst+count]* [src...src+count]* * 情况2: dst >= src+count (不重叠) * [src...src+count] [dst...dst+count]*/while (count--) {// 逐字节复制:源数据 → 目标位置*(char *)dst = *(char *)src;// 指针向前移动一个字节dst = (char *)dst + 1;src = (char *)src + 1;}}else {/** 重叠缓冲区(正向重叠)* 从高地址到低地址复制(反向复制)* * 内存布局示例:* [src.........src+count]* [dst.........dst+count]* * 必须反向复制以避免覆盖尚未复制的源数据*/// 将指针移动到各自内存块的末尾dst = (char *)dst + count - 1;src = (char *)src + count - 1;// 从后往前逐字节复制while (count--) {// 逐字节复制:从末尾开始向开头复制*(char *)dst = *(char *)src;// 指针向后移动一个字节dst = (char *)dst - 1;src = (char *)src - 1;}}// 返回原始的目标指针return ret;
}