📚 第18讲:C语言内存函数
深入理解底层内存操作,掌握高效、安全的数据拷贝与比较技术!
📂 目录
memcpy
的使用与模拟实现 🧩memmove
的使用与模拟实现 🔄memset
函数的使用 🎨memcmp
函数的使用 ⚖️
📖 正文开始
在C语言中,除了字符串操作函数外,还有一组内存函数 ,它们不关心数据类型,而是以字节为单位 直接操作内存。这些函数定义在 <string.h>
头文件中,适用于任意类型的数据块操作。
memcpy
的使用与模拟实现 🧩
c
void *memcpy(void *destination, const void *source, size_t num);
🔹 特性说明
- 从
source
指向的内存位置开始,复制num
个字节 到destination
。 - 不会 因遇到
'\0'
而停止。 - 如果
source
和destination
指向的内存区域有重叠 ,行为是未定义的(undefined behavior)。
✅ 适用场景 :源和目标内存不重叠时的高效拷贝。
✅ 示例:整型数组拷贝
c
#include <stdio.h>
#include <string.h>
int main() {
int arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int arr2[10] = {0};
// 拷贝前20个字节(5个int)
memcpy(arr2, arr1, 20);
for (int i = 0; i < 10; i++) {
printf("%d ", arr2[i]);
}
printf("\n");
return 0;
}
📌 输出:
1 2 3 4 5 0 0 0 0 0
🔧 memcpy
模拟实现
c
#include <stdio.h>
#include <assert.h>
void *my_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;
}
return ret;
}
int main() {
int arr1[] = {10, 20, 30, 40};
int arr2[4] = {0};
my_memcpy(arr2, arr1, sizeof(arr1));
for (int i = 0; i < 4; i++) {
printf("%d ", arr2[i]);
}
printf("\n");
return 0;
}
📌 输出:
10 20 30 40
⚠️ 注意 :此函数不能处理内存重叠!重叠场景请使用memmove
。
memmove
的使用与模拟实现 🔄
c
void *memmove(void *destination, const void *source, size_t num);
🔹 特性说明
- 功能与
memcpy
类似,但可以正确处理源和目标内存区域重叠的情况。 - 是处理重叠内存拷贝的安全选择。
✅ 示例:数组内部移动(内存重叠)
c
#include <stdio.h>
#include <string.h>
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 将前5个元素(20字节)向前移动2个位置
memmove(arr + 2, arr, 20);
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
📌 输出:
1 2 1 2 3 4 5 8 9 10
🔍 解析:
- 原数组:
[1,2,3,4,5,6,7,8,9,10]
- 将
arr[0]~arr[4]
拷贝到arr[2]~arr[6]
- 使用
memmove
可避免因重叠导致的数据覆盖错误。
🔧 memmove
模拟实现
c
#include <assert.h>
void *my_memmove(void *dst, const void *src, size_t count) {
void *ret = dst;
assert(dst != NULL);
assert(src != NULL);
if (dst <= src || (char *)dst >= ((char *)src + count)) {
// ✅ 情况1:无重叠 或 重叠但目标在源之后
// 从低地址向高地址拷贝
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
} else {
// ✅ 情况2:重叠且目标在源之前
// 从高地址向低地址拷贝,避免覆盖
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;
}
int main() {
char str[] = "hellohello";
my_memmove(str + 5, str, 5); // 重叠拷贝
printf("Result: %s\n", str); // 输出: hellohello
return 0;
}
✅ 关键点 :通过判断内存地址关系,选择正向或反向拷贝,确保数据安全。
memset
函数的使用 🎨
c
void *memset(void *ptr, int value, size_t num);
🔹 特性说明
- 将
ptr
指向的内存区域的前num
个字节 ,设置为value
(按字节填充)。 - 常用于初始化数组、结构体等。
value
是int
类型,但只使用其低8位(一个字节)。
✅ 示例:字符数组填充
c
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello world";
memset(str, 'x', 6); // 前6个字符设为 'x'
printf("Result: %s\n", str); // 输出: xxxxxxworld
return 0;
}
✅ 示例:整型数组初始化为0
c
#include <stdio.h>
#include <string.h>
int main() {
int arr[5];
memset(arr, 0, sizeof(arr)); // 全部设为0
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
📌 输出:
0 0 0 0 0
⚠️ 注意 :memset
是按字节设置的。若想将int
数组设为-1
,可以:
cmemset(arr, -1, sizeof(arr)); // 每个字节都是 0xFF,int 变为 -1
memcmp
函数的使用 ⚖️
c
int memcmp(const void *ptr1, const void *ptr2, size_t num);
🔹 特性说明
- 比较从
ptr1
和ptr2
开始的num
个字节。 - 按字节进行无符号比较。
- 返回值:
> 0
:ptr1
大于ptr2
== 0
:前num
字节完全相同< 0
:ptr1
小于ptr2
✅ 示例:比较两个缓冲区
c
#include <stdio.h>
#include <string.h>
int main() {
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
// 比较整个字符串(包含 '\0')
n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0) {
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
} else if (n < 0) {
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
} else {
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
}
return 0;
}
📌 输出:
'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.
🔍 解析 :比较是逐字节进行的。第一个不同字符是'g'
vs'G'
,由于'g' > 'G'
(ASCII值),所以返回正值。
✅ 示例:比较结构体或任意数据块
c
#include <stdio.h>
#include <string.h>
struct Data {
int id;
float score;
};
int main() {
struct Data d1 = {1001, 89.5f};
struct Data d2 = {1001, 89.5f};
if (memcmp(&d1, &d2, sizeof(struct Data)) == 0) {
printf("Two data structs are identical.\n");
} else {
printf("Data structs differ.\n");
}
return 0;
}
📌 输出:
Two data structs are identical.
✅ 核心知识点总结
函数 | 功能 | 是否支持重叠 | 关键点 |
---|---|---|---|
memcpy |
内存拷贝 | ❌ 不支持 | 高效,但重叠时行为未定义 |
memmove |
内存移动 | ✅ 支持 | 安全处理重叠,稍慢 |
memset |
内存设置 | --- | 按字节填充,常用于初始化 |
memcmp |
内存比较 | --- | 按字节比较,返回差值 |
🎯 掌握这些内存函数,你就能像操作系统一样直接操控内存,写出更高效、底层的C代码!
下节我们将学习动态内存管理,敬请期待!💪