C语言入门指南:内存操作函数详解
目录
引言
1. memcpy:内存复制的“快刀手”
1.1函数原型:
1.2 功能:
1.3 重要特性:
1.4示例:
1.5 模拟实现(理解原理)
2. memmove:处理内存重叠的“安全卫士”
2.1 函数原型:
2.2 功能:
什么时候会内存重叠?
举个栗子 🌰
2.3 模拟实现(理解其智能之处)
3. memset:内存批量“填充器”
3.1 函数原型:
3.2功能:
3.3重要特性:
3.4示例:
4 memcmp:内存“比较器”
4.1 函数原型
4.2 功能:
4.3 返回值
举个栗子 🌰
总结:
引言
大家好!在C语言的编程世界里,除了我们熟悉的字符串函数(如strcpy
, strlen
),还有一类非常强大且底层的函数——内存操作函数。它们不关心你操作的数据是什么类型(是字符、整数还是结构体),只把内存看作是一块连续的字节空间,直接对字节进行复制、设置或比较。掌握这些函数,能让你的程序更高效,也能让你更深入地理解计算机内存的工作原理。
今天,我们就来详细聊聊四个核心的内存函数:
memcpy
, memmove
, memset ,
memcmp
。
我会用最通俗的语言和清晰的例子,带你搞懂它们!
1. memcpy
:内存复制的“快刀手”
1.1函数原型:
void* memcpy( void* destination, const void* source, size_t num );
1.2 功能:
memcpy
的作用非常直接:
从源地址source
开始,向后复制num
个字节的数据,到目标地址destination
。
1.3 重要特性:
- 不认
\0
:和strcpy
不同,memcpy
不会因为遇到\0
(空字符)就停下来。它严格按照你指定的字节数num
进行复制。 - 不能处理内存重叠:这是
memcpy
最大的“雷区”!如果源内存区域和目标内存区域有重叠部分,memcpy
的行为是未定义的,结果可能完全错误。
1.4示例:
假设我们有一个整型数组arr1
,想把它的前5个元素(即前20个字节,因为一个int
通常是4字节)复制到另一个数组arr2
中。
#include <stdio.h>
#include <string.h> // memcpy在string.h头文件中int main() {int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int arr2[10] = { 0 }; // 初始化为0// 复制20个字节,即5个intmemcpy(arr2, arr1, 20);// 打印arr2for (int i = 0; i < 10; i++) {printf("%d ", arr2[i]);}// 输出:1 2 3 4 5 0 0 0 0 0return 0;
}
1.5 模拟实现(理解原理)
理解memcpy
最好的方式就是自己动手写一个!它的核心思想就是逐字节拷贝。
#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和src强制转换为char*,以便按字节访问dst = (char*)dst + 1; // 指针向后移动一个字节src = (char*)src + 1;}return ret;
}
关键点:我们把void*
指针强制转换为char*
,因为char
类型正好占1个字节,这样就能精确地按字节进行操作了。
2. memmove
:处理内存重叠的“安全卫士”
2.1 函数原型:
void* memmove( void* destination, const void* source, size_t num );
2.2 功能:
memmove
的功能和memcpy
一模一样,都是复制num
个字节。但它的核心优势在于:即使源内存和目标内存有重叠,它也能保证复制结果的正确性!
什么时候会内存重叠?
最常见的场景就是在同一个数组内进行元素移动。比如,你想把数组的前几个元素挪到后面去。
举个栗子 🌰
我们尝试用memmove
把数组arr1
的前5个元素,复制到从第3个元素开始的位置
#include <stdio.h>
#include <string.h>int main() {int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };// 把前5个元素(20字节)复制到从第3个元素(下标为2)开始的位置// 注意:这里源区域[0-4]和目标区域[2-6]明显重叠了!memmove(arr1 + 2, arr1, 20);for (int i = 0; i < 10; i++) {printf("%d ", arr1[i]);}// 输出:1 2 1 2 3 4 5 8 9 10// 解释:前两个元素没动,从第三个开始被前五个元素覆盖。return 0;
}
如果你在这里用memcpy
,结果将是不可预测的,可能是一堆乱码!
2.3 模拟实现(理解其智能之处)
memmove
之所以能处理重叠,是因为它很“聪明”,会根据内存重叠的情况选择不同的拷贝方向。
#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;
}
核心思想:
- 如果目标区域在源区域“后面”或者完全不重叠,就从前往后拷贝。
- 如果目标区域在源区域“前面”且有重叠,就从后往前拷贝,这样就不会把还没复制的源数据给覆盖掉。
3. memset
:内存批量“填充器”
3.1 函数原型:
void* memset( void* ptr, int value, size_t num );
3.2功能:
memset
的作用是将某一块内存的前num
个字节,全部设置为指定的值value
。
3.3重要特性:
value
虽然是int
类型,但实际使用时,它会被转换为unsigned char
(即只取其最低的8位,一个字节)。- 它是按字节设置的,所以最适合用于初始化为0,或者初始化字符数组。
3.4示例:
#include <stdio.h>
#include <string.h>int main() {char str[] = "hello world"; // 这是一个字符数组printf("Before: %s\n", str);// 将str的前6个字节设置为字符 'x'memset(str, 'x', 6);printf("After: %s\n", str);// 输出:// Before: hello world// After: xxxxxxworldreturn 0;
}
一个非常常见的用法:初始化数组为0
int arr[100];
// 将整个数组(100 * sizeof(int) 个字节)初始化为0
memset(arr, 0, sizeof(arr));
注意:memset
按字节赋值,所以用它给int
数组赋非0值要小心!比如memset(arr, 1, sizeof(arr))
,并不会让每个int
变成1,而是让每个字节都变成0x01
,最终每个int
会变成0x01010101
(即十进制的16843009),这通常不是你想要的结果。
4 memcmp
:内存“比较器”
4.1 函数原型
int memcmp( const void* ptr1, const void* ptr2, size_t num );
4.2 功能:
memcmp
用于比较从ptr1
和ptr2
指针开始的num
个字节。
4.3 返回值
- < 0:
ptr1
指向的内存区域 小于ptr2
指向的内存区域。 - = 0: 两者相等。
- > 0:
ptr1
指向的内存区域 大于ptr2
指向的内存区域。
比较规则是逐字节比较,和strcmp
类似,遇到不同的字节就返回差值,如果都相同则返回0。
举个栗子 🌰
#include <stdio.h>
#include <string.h>int main() {char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";// 比较两个数组的所有字节int 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);elseprintf("'%s' is the same as '%s'.\n", buffer1, buffer2);// 输出:'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.// 原因:在第3个字节,'g' (ASCII 103) > 'G' (ASCII 71)return 0;
}
总结:
一句话建议:如果你不确定内存是否会重叠,无脑使用memmove
!它在功能上完全兼容memcpy
,只是在有重叠时多做了一点判断,安全性满分。
希望这篇博客能帮你彻底掌握这四个强大的内存函数!它们是C语言底层操作的基石,理解它们,你的编程功力会更上一层楼!