当前位置: 首页 > news >正文

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),这通常不是你想要的结果。

memcmp:内存“比较器”

4.1 函数原型

int memcmp( const void* ptr1, const void* ptr2, size_t num );

4.2 功能:

memcmp用于比较从ptr1ptr2指针开始的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语言底层操作的基石,理解它们,你的编程功力会更上一层楼!

            

http://www.dtcms.com/a/390884.html

相关文章:

  • React 列表渲染 列表排序 条件渲染 数据渲染 响应式处理
  • 从安卓手机切换到iPhone:好处、缺点及4种方法
  • C++ 篇 类和对象(1)万能工具怎么用?
  • Ansible-copy模块
  • SAPO去中心化训练:多节点协作让LLM训练效率提升94%
  • Stm32 IAP 升级
  • 5G标准学习笔记17------ MDT(Minimization of Drive Tests)路测最小化
  • [Dify] 构建“流程型表单问答”系统:逐步提问逻辑实现
  • 从RAW到JPG到BMP:工业视觉图像格式怎么选?
  • Linux系统Rsync+sersync 实现数据同步
  • 【13/20】缓存与性能优化:Redis 在 Express 中的整合,实现用户数据缓存
  • 如何防止电脑长时间运行过热?定时关机是第一道防线
  • 开源监控利器Prometheus+Grafana在银河麒麟操作系统的落地实践
  • 小程序移动端设计UI(一)预约小程序——东方仙盟练气期
  • Android13 命令启用WLAN详细日志分析
  • 临床AI产品化全流程研究:环境聆听、在环校验与可追溯系统的多技术融合实践(中)
  • 深度解读昇腾CANN动态Shape图调度加速技术
  • linux系统使用ImageMagick注意,只能使用convert命令
  • [Windows] 搜狗拼音一键净化
  • Go语言25个关键字全解析
  • 图像滤波常用总结
  • Go语言设计原则与设计模式
  • (LoRA深度解析)LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS论文精读(逐段解析)
  • 第十四届蓝桥杯青少组C++选拔赛[2022.11.27]第二部分编程题(4、找路线)
  • 知识图谱对自然语言处理深层语义分析的影响与启示:结构化研究报告
  • 架构师成长之路-缓存二
  • 正点原子小智BOX0/BOX2 产品使用视频表情功能
  • 鸿蒙NEXT分布式文件系统:开启跨设备文件访问新时代
  • 【主机初始化工作】
  • Ubuntu20.04仿真 | iris四旋翼添加livox mid360激光雷达