36 C 语言内存操作函数详解:memset、memcpy、memccpy、memmove、memcmp、memchr
1 memset() 函数
1.1 函数原型
#include <string.h> // 必须包含此头文件才能使用 memsetvoid *memset(void *s, int c, size_t n);
1.2 功能说明
memset 用于将指定内存块的前 n 个字节设置为指定的值。常用于初始化内存(如清零缓冲区或填充特定值)。
- 参数:
- s:指向要填充的内存块的指针。
- c:要设置的值(以 int 形式传递,但实际填充的是 unsigned char 类型,范围 0 ~ 255)。
- n:要填充的字节数。
- 返回值:
- 返回指向 s 的指针(即目标内存块的起始地址),通常不需要显式使用返回值。
1.3 注意事项
- 类型转换:c 参数虽然是 int 类型,但实际填充时会截断为 unsigned char(即只使用低 8 位)。
- 性能:memset 通常由编译器或标准库高度优化,适合快速初始化大块内存。
- 安全性:确保目标内存块的大小足够大,避免缓冲区溢出。
- 用途限制:适合填充单字节值,不适合初始化结构体中的多字节字段(如整数或浮点数)。
1.4 应用场景
1. 内存清零:初始化数组或结构体为全 0。
// 声明一个字符数组char buf[100];// 使用 memset 将数组内存清零memset(buf, 0, sizeof(buf));// 定义一个结构体
typedef struct
{int id;char name[50];float score;
} Student;// 声明一个 Student 结构体变量Student student;// 使用 memset 将结构体内存清零memset(&student, 0, sizeof(student));
2. 填充特定值:将缓冲区填充为特定字符(如空格或特定标记)。
char str[20];
memset(str, '-', 19); // 填充 19 个 '-'
str[19] = '\0'; // 手动添加字符串结束符
3. 重置敏感数据:在释放内存前,用 memset 清零敏感数据(如密码缓冲区)。
// 在释放内存前,用 memset 清零密码缓冲区
memset(password_buffer, 0, sizeof(password_buffer));
1.5 示例程序
#include <stdio.h>
#include <string.h> // 必须包含此头文件才能使用 memsetint main()
{char buffer[10];// 初始化 buffer 为全 0memset(buffer, 0, sizeof(buffer));printf("调用 memset 后(全零): %s\n", buffer);// 填充 buffer 为 'A'memset(buffer, 'A', 9);buffer[9] = '\0'; // 添加字符串结束符printf("调用 memset 后('A'): %s\n", buffer);return 0;
}
程序在 VS Code 中的运行结果如下所示:
2 memcpy() 函数
2.1 函数原型
#include <string.h> // 必须包含此头文件才能使用 memcpyvoid *memcpy(void *dest, const void *src, size_t n);
2.2 功能说明
memcpy 用于将源内存块的前 n 个字节复制到目标内存块。常用于内存数据的拷贝(如数组、结构体等)。
- 参数:
- dest:指向目标内存块的指针,用于存储复制的数据。
- src:指向源内存块的指针,提供要复制的数据。
- n:要复制的字节数。
- 返回值:
- 返回指向 dest 的指针(即目标内存块的起始地址),通常不需要显式使用返回值。
2.3 注意事项
- 内存重叠:memcpy 不处理内存重叠的情况。如果源和目标内存块有重叠,应使用 memmove 函数。
- 性能:memcpy 通常由编译器或标准库高度优化,适合快速复制大块内存。
- 安全性:确保目标内存块的大小足够大,避免缓冲区溢出。
- 类型安全:确保源和目标内存块的类型兼容,避免未定义行为。
2.4 应用场景
1. 复制数组:将一个数组的内容复制到另一个数组。
int src[5] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, sizeof(src)); // 复制 src 到 dest
2. 复制结构体:将一个结构体的内容复制到另一个结构体。
typedef struct
{int id;char name[20];
} Person;Person p1 = {1, "Alice"};
Person p2;
memcpy(&p2, &p1, sizeof(Person)); // 复制 p1 到 p2
3. 缓冲区操作:在网络编程或文件操作中,复制缓冲区数据。
char src_buf[100] = "Hello, world!";
char dest_buf[100];
memcpy(dest_buf, src_buf, strlen(src_buf) + 1); // 复制字符串(包括 '\0')
// strlen 返回字符串长度,不包括 '\0',所以需要加 1
2.5 示例程序
#include <stdio.h>
#include <string.h> // 必须包含此头文件才能使用 memcpy 和 strlen 函数int main()
{char src[] = "Hello, memcpy!";char dest[20];// 复制 src 到 destmemcpy(dest, src, strlen(src) + 1);// strlen(src) 返回字符串的长度,+1 是为了复制字符串的终止符 '\0'printf("复制后的字符串: %s\n", dest);// 复制部分数据int nums[5] = {1, 2, 3, 4, 5};int copied_nums[3];memcpy(copied_nums, nums, 3 * sizeof(int)); // 复制前 3 个整数printf("复制的部分数组: %d, %d, %d\n", copied_nums[0], copied_nums[1], copied_nums[2]);return 0;
}
程序在 VS Code 中的运行结果如下所示:
3 memccpy() 函数
3.1 函数原型
#include <string.h> // 必须包含此头文件才能使用 memccpyvoid *memccpy(void *dest, const void *src, int c, size_t n);
3.2 功能说明
memccpy 用于将源内存块的内容复制到目标内存块,直到遇到指定的字符 c 或复制了 n 个字节为止。常用于复制字符串或内存块,直到遇到特定终止符。
- 参数:
- dest:指向目标内存块的指针,用于存储复制的数据。
- src:指向源内存块的指针,提供要复制的数据。
- c:要查找的终止字符(以 int 形式传递,但实际比较的是 unsigned char 类型)。
- n:要复制的最大字节数。
- 返回值:
- 如果在复制过程中遇到字符 c,则返回指向 dest 中字符 c 之后位置的指针。
- 如果未遇到字符 c 且复制了 n 个字节,则返回 NULL。
3.3 注意事项
- 终止字符:memccpy 会在复制过程中检查是否遇到字符 c,如果遇到则停止复制。
- 性能:memccpy 的性能通常不如 memcpy,因为它需要额外检查字符。
- 安全性:确保目标内存块的大小足够大,避免缓冲区溢出。
- 类型安全:确保源和目标内存块的类型兼容,避免未定义行为。
3.4 应用场景
1. 复制字符串直到终止符:复制字符串直到遇到 \0 或指定字符。
char src[] = "Hello, memccpy!";
char dest[20];
memccpy(dest, src, '!', sizeof(dest)); // 复制直到 '!' 或缓冲区满
2. 解析特定格式的数据:从内存块中提取数据,直到遇到分隔符。
char data[] = "123,456,789";
char token[10];
memccpy(token, data, ',', sizeof(token)); // 复制直到 ',' 或缓冲区满
3.5 示例程序
#include <stdio.h>
#include <string.h> // 必须包含此头文件才能使用 memccpyint main()
{char src[] = "Hello, memccpy!";char dest[20];// 复制 src 到 dest,直到遇到 '!' 或缓冲区满void *result = memccpy(dest, src, '!', sizeof(dest));if (result != NULL){printf("复制终止于 '!',结果: %s\n", dest);// 如果在复制过程中遇到字符 !,则返回指向 dest 中字符 ! 之后位置的指针printf("终止字符的下一个位置: %p \n也就是指向 dest[16] 的位置:%p\n", result, &dest[15]);}else{printf("未找到终止符 '!',复制了部分数据: %s\n", dest);}char data[] = "123,456,789";char token[10];// 复制 data 到 token,直到遇到 ',' 或缓冲区满result = memccpy(token, data, ',', sizeof(token));if (result != NULL){printf("复制终止于 ',',结果: %s\n", token);}else{printf("未找到终止符 ',',复制了部分数据: %s\n", token);}return 0;
}
程序在 VS Code 中的运行结果如下所示:
4 memmove() 函数
4.1 函数原型
#include <string.h> // 必须包含此头文件才能使用 memmovevoid *memmove(void *dest, const void *src, size_t n);
4.2 功能说明
memmove 用于将源内存块的前 n 个字节复制到目标内存块。与 memcpy 不同,memmove 可以正确处理源和目标内存块重叠的情况。
- 参数:
- dest:指向目标内存块的指针,用于存储复制的数据。
- src:指向源内存块的指针,提供要复制的数据。
- n:要复制的字节数。
- 返回值:
- 返回指向 dest 的指针(即目标内存块的起始地址),通常不需要显式使用返回值。
4.3 注意事项
- 内存重叠:memmove 可以正确处理源和目标内存块重叠的情况。
- 性能:由于需要处理内存重叠,memmove 的性能通常略低于 memcpy。
- 安全性:确保目标内存块的大小足够大,避免缓冲区溢出。
- 类型安全:确保源和目标内存块的类型兼容,避免未定义行为。
4.4 应用场景
1. 复制重叠内存块:当源和目标内存块可能重叠时,使用 memmove 确保数据正确复制。
char buffer[20] = "1234567890";
memmove(buffer + 3, buffer, 5); // 将前 5 个字符复制到从第 3 个位置开始的地方
printf("复制后的缓冲区: %s\n", buffer); // 输出: 1231234590
2. 安全移动数据:在需要移动或调整内存数据时,使用 memmove 确保数据完整性。
int nums[5] = {1, 2, 3, 4, 5};
memmove(nums + 1, nums, 3 * sizeof(int)); // 将前 3 个整数移动到从第 2 个位置开始的地方
printf("移动后的数组: %d, %d, %d, %d, %d\n", nums[0], nums[1], nums[2], nums[3], nums[4]); // 输出: 1, 1, 2, 3, 4
4.5 示例程序
#include <stdio.h>
#include <string.h> // 必须包含此头文件才能使用 memmoveint main()
{char buffer[20] = "1234567890";// 复制重叠内存块memmove(buffer + 3, buffer, 5);printf("复制后的缓冲区: %s\n", buffer); // 输出: 1231234590// 移动整数数组int nums[5] = {1, 2, 3, 4, 5};memmove(nums + 1, nums, 3 * sizeof(int));printf("移动后的数组: %d, %d, %d, %d, %d\n", nums[0], nums[1], nums[2], nums[3], nums[4]); // 输出: 1, 1, 2, 3, 5return 0;
}
程序在 VS Code 中的运行结果如下所示:
5 memcmp() 函数
5.1 函数原型
#include <string.h> // 必须包含此头文件才能使用 memcmpint memcmp(const void *s1, const void *s2, size_t n);
5.2 功能说明
memcmp 用于比较两个内存块的前 n 个字节,判断它们是否相等或哪个更大。常用于比较二进制数据(如数组、结构体等)。
- 参数:
- s1:指向第一个内存块的指针。
- s2:指向第二个内存块的指针。
- n:要比较的字节数。
- 返回值:
- 如果 s1 和 s2 的前 n 个字节相等,返回 0。
- 如果 s1 小于 s2(按字典序),返回负值。
- 如果 s1 大于 s2(按字典序),返回正值。
5.3 注意事项
- 比较方式:memcmp 按字节逐个比较,比较的是内存中的二进制值,而不是字符串的逻辑值(例如,'A' 和 'a' 的 ASCII 值不同,会被认为不相等)。
- 性能:memcmp 通常由编译器或标准库高度优化,适合快速比较大块内存。
- 安全性:确保 s1 和 s2 指向的内存块至少有 n 个字节,避免越界访问。
- 类型安全:memcmp 比较的是内存中的原始字节,与数据类型无关,因此适用于任何类型的内存块。
5.4 应用场景
1. 比较数组:比较两个数组的前 n 个元素是否相等。
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5] = {1, 2, 3, 4, 6};int result = memcmp(arr1, arr2, 5 * sizeof(int)); // 比较前 5 个整数if (result < 0)
{printf("arr1 小于 arr2\n");
}
else if (result > 0)
{printf("arr1 大于 arr2\n");
}
else
{printf("arr1 等于 arr2\n");
}
2. 比较结构体:比较两个结构体的内容是否相等。
typedef struct
{int id;char name[20];
} Person;Person p1 = {1, "Alice"};
Person p2 = {1, "Alice"};int result = memcmp(&p1, &p2, sizeof(Person)); // 比较两个结构体if (result == 0)
{printf("p1 等于 p2\n");
}
3. 验证数据完整性:比较接收到的数据与预期数据是否一致。
char expected[] = {0x01, 0x02, 0x03, 0x04};
char received[] = {0x01, 0x02, 0x03, 0x05};int result = memcmp(expected, received, sizeof(expected));if (result != 0)
{printf("数据不一致!\n");
}
5.5 示例程序
#include <stdio.h>
#include <string.h> // 必须包含此头文件才能使用 memcmpint main()
{// 比较数组int arr1[5] = {1, 2, 3, 4, 5};int arr2[5] = {1, 2, 3, 4, 6};// 比较前 5 个元素int result = memcmp(arr1, arr2, 5 * sizeof(int));if (result < 0){printf("arr1 小于 arr2\n");}else if (result > 0){printf("arr1 大于 arr2\n");}else{printf("arr1 等于 arr2\n");}// 比较结构体typedef struct{int id;char name[20];} Person;Person p1 = {1, "Alice"}; // 初始化结构体变量Person p2 = {1, "Alice"}; // 初始化结构体变量// 比较整个结构体result = memcmp(&p1, &p2, sizeof(Person));if (result == 0){printf("p1 等于 p2\n");}// 比较字符串(注意:memcmp 比较的是二进制值)char str1[] = "Hello";char str2[] = "hello";/*'H' (0x48,二进制 01001000) 和 'h' (0x68,二进制 01101000)。0x48('H')的二进制值小于 0x68('h'),因此 memcmp 会立即返回一个负值,表示 "Hello" 的第一个字节小于 "hello" 的第一个字节。*/result = memcmp(str1, str2, 5); // 比较前 5 个字符if (result < 0){printf("str1 小于 str2(按二进制值比较)\n");}return 0;
}
程序在 VS Code 中的运行结果如下所示:
6 memchr() 函数
6.1 函数原型
#include <string.h> // 必须包含此头文件才能使用 memchrvoid *memchr(const void *s, int c, size_t n);
6.2 功能说明
memchr 用于在内存块的前 n 个字节中搜索特定字节值的首次出现位置。它逐字节扫描内存块,直到找到匹配的字节或扫描完指定的字节数 n。
- 参数:
- s:指向要搜索的内存块的指针。
- c:要搜索的字节值(以 int 形式传递,但实际比较的是 unsigned char 类型,范围 0 ~ 255)。
- n:要搜索的字节数。
- 返回值:
- 如果找到匹配的字节,返回指向该位置的指针。
- 如果未找到匹配的字节,返回 NULL。
6.3 注意事项
- 类型转换:c 参数虽然是 int 类型,但实际比较时会转换为 unsigned char。
- 性能:memchr 通常由编译器或标准库高度优化,适合快速搜索大块内存。
- 安全性:确保搜索范围不超过目标内存块的实际大小,避免越界访问。
- 大小写敏感:搜索是区分大小写的,'A' 和 'a' 会被视为不同的值。
6.4 应用场景
1. 查找特定字符:在字符串或缓冲区中查找特定字符的位置。
char text[] = "Programming in C";
char *found = memchr(text, 'a', strlen(text));if (found)
{printf("找到字符 'a',位置在: %lld\n", found - text);
}
else
{printf("未找到字符 'a'\n");
}
2. 检查二进制数据中是否存在特定标记。
unsigned char data[] = {0x01, 0x02, 0xFF, 0x03};if (memchr(data, 0xFF, sizeof(data)))
{printf("数据中包含 0xFF 标记\n");
}
3. 快速定位缓冲区中的分隔符。
char buffer[] = "data1,data2,data3";
char *sep = memchr(buffer, ',', strlen(buffer));if (sep)
{printf("找到分隔符 ',' 在位置: %lld\n", sep - buffer);
}
6.5 示例程序
#include <stdio.h>
#include <string.h> // 必须包含此头文件才能使用 memchrint main()
{char str[] = "This is a sample string for memchr demonstration";// 查找空格字符 ' 'char *space_pos = memchr(str, ' ', strlen(str));if (space_pos){printf("找到空格字符,位置在: %lld\n", space_pos - str);printf("找到的字符是: '%c'\n", *space_pos);}else{printf("未找到空格字符\n");}// 查找字母 'm'char *m_pos = memchr(str, 'm', strlen(str));if (m_pos){printf("找到字母 'm',位置在: %lld\n", m_pos - str);printf("找到的字符是: '%c'\n", *m_pos);}else{printf("未找到字母 'm'\n");}// 查找不存在的字符 'X'char *x_pos = memchr(str, 'X', strlen(str));if (!x_pos){printf("未找到字符 'X'\n");}return 0;
}
程序在 VS Code 中的运行结果如下所示:
7 内存操作函数总结
函数名 | 功能描述 | 参数 | 返回值 |
---|---|---|---|
memset | 将内存块的所有字节设置为指定值 | void *s(目标内存块指针), int c(要设置的值), size_t n(要设置的字节数) | 返回目标内存块的指针 |
memcpy | 从源内存块复制字节到目标内存块 | void *dest(目标内存块指针), const void *src(源内存块指针), size_t n(复制的字节数) | 返回目标内存块的指针 |
memccpy | 从源内存块复制字节到目标内存块,直到遇到指定字符或复制了指定字节数 | void *dest(目标内存块指针), const void *src(源内存块指针), int c(要查找的字符), size_t n(复制的字节数) | 遇到字符时返回指向目标内存块中该字符之后位置的指针,否则返回 NULL |
memmove | 从源内存块复制字节到目标内存块,正确处理内存重叠 | void *dest(目标内存块指针), const void *src(源内存块指针), size_t n(复制的字节数) | 返回目标内存块的指针 |
memcmp | 比较两个内存块的内容 | const void *s1(第一个内存块指针), const void *s2(第二个内存块指针), size_t n(比较的字节数) | 如果 s1 小于、等于或大于 s2,分别返回负整数、零或正整数 |
memchr | 在内存块中搜索特定字节值的首次出现位置 | const void *s(目标内存块指针), int c(要搜索的字节值), size_t n(扫描的字节数) | 找到时返回指向该字节的指针,否则返回 NULL |
其他类似但非标准 C 库的函数:
- bcmp:类似于 memcmp,用于比较两个内存块的内容,但通常不是标准 C 库的一部分。
- bcopy:类似于 memcpy 或 memmove,用于复制内存块,但通常不是标准 C 库的一部分。
- bzero:类似于 memset,用于将内存块的所有字节设置为 0,但通常不是标准 C 库的一部分。
- memicmp:类似于 memcmp,但不区分大小写,通常不是标准 C 库的一部分。
- movmem:类似于 memmove,用于复制内存块,可能包含额外的功能或优化,通常不是标准 C 库的一部分。
- setmem:类似于 memset,用于将内存块的所有字节设置为指定值,可能包含额外的功能或优化,通常不是标准 C 库的一部分。