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

系统性学习C语言-第十八讲-C语言内存函数

系统性学习C语言-第十八讲-C语言内存函数

  • 1. memcpy 使用和模拟实现
  • 2. memmove 使用和模拟实现
  • 3. memset 函数的使用
  • 4. memcmp 函数的使用

1. memcpy 使用和模拟实现

在这里插入图片描述

void * memcpy ( void * destination, const void * source, size_t num );

memcpy 函数,又称内存拷贝函数,将源数组中的内容拷贝到目标数组中,无论数组的类型,均能进行拷贝操作。

num 为要拷贝的字节数。

下面是使用 memcpy 函数的一些注意事项:

  1. 函数 memcpysource 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。

  2. 这个函数在遇到 '\0' 的时候并不会停下来。

  3. 如果 sourcedestination 有任何的重叠,复制的结果都是未定义的。

函数使用举例:

#include <stdio.h>
#include <string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;
}

对于重叠的内存,交给 memmove 函数来处理。

memcpy 函数的模拟实现:

void * memcpy ( void * dst, const void * src, size_t count)
{void * ret = dst;assert(dst);assert(src);while (count--) {*(char *)dst = *(char *)src;dst = (char *)dst + 1;src = (char *)src + 1;}return(ret);
}

在对 memcpy 函数的模拟实现中,要解决的其中一个难点就是如何将不同类型的数组,用一样的代码来处理,

首先就要从参数部分下手,参数部分我们要将指针的类型设置成 void* ,这样所有类型的数组我们都能进行接收,

接下来就是处理数组的拷贝问题,void* 的指针是不能直接进行解引用的,这意味着我们必须要转换指针类型,

这时我们的参数还有字节数,这就说明在我们将 void* 指针类型强转为 char* 后,可以根据传入的字节数大小去兼职循环的次数。

所以对于拷贝的操作我们只需将指针强转,然后解引用赋值即可,但是我们仍然要解决指针的移动问题,

void* 类型的指针是不能进行自增的,所以这也意味着我们要进行强转,但是 *(char *)dst++ 强转后的自增是不可以的,

这样的代码相当于没有强转,所以我们只能采用强转赋值的方法去进行处理, dst = (char *)dst + 1; ,这样模拟实现的难点就都解决了。

2. memmove 使用和模拟实现

在这里插入图片描述

void * memmove ( void * destination, const void * source, size_t num );

memmove 内存移动函数,用于将源数组的内容拷贝放到目标数组中,与 memcopy 函数不同的是,

在 C语言 规定中 memmove 函数可以处理内存重叠部分的内容移动。

下面是使用 memmove 函数的一些注意事项:

  1. memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。

  2. 如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。

函数使用示例:

#include <stdio.h>
#include <string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1+2, arr1, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

memmove 的模拟实现:

void * memmove ( void * dst, const void * src, size_t count)
{void * ret = dst;if (dst <= src || (char *)dst >= ((char *)src + count)) {/** Non-Overlapping Buffers* copy from lower addresses to higher addresses*/while (count--) {*(char *)dst = *(char *)src;dst = (char *)dst + 1;src = (char *)src + 1;}}else {/** Overlapping Buffers* copy from higher addresses to lower addresses*/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);
}

对于 memmove 函数的模拟实现,难点在于我们如何处理重叠区域的内存移动,

我们先来分析在不同的情况下,重叠区域应该如何来移动:

在这里插入图片描述
在源数组与目标数组重叠部分在元素组偏后的元素时,我们如果先将源数组的内容从前往后放置到目标数组中,当 2 覆盖 5 后,

我们如果要将源数组的 5 覆盖目标数组的 8 时,我们就会发现此时 5 已经变成 2 ,无法达到我们的我目的,

所以我们要将源数组的内容从后往前放置到目标数组中,先将重叠区域的内容放置到目标数组中,避免被覆写后值改变。

所以目标数组与源数组重叠区域在源数组元素偏后时,我们就从后往前覆写。

在这里插入图片描述
跟上面一样进行分析后,当目标数组与源数组重叠区域在源数组元素偏前时,我们就从前往后覆写,避免重叠区域先被覆写。

当目标数组与源数组没有任何交集时,选择从前往后、从后往前的覆写方式均可以。这个难点被解决后,memmove 的模拟便再无难点

3. memset 函数的使用

在这里插入图片描述

void * memset ( void * ptr, int value, size_t num );

memset 是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。

函数使用示范:

#include <stdio.h>
#include <string.h>
int main ()
{char str[] = "hello world";memset (str,'x',6);printf(str);return 0;
}

对于 memset 函数在使用时我们一定要注意一点,对于这样的代码:

int arr[10] = {0};
memset (arr, 1, 4);

我们可千万不能认为是将 arr 数组的前四个元素置为 1 ,因为 memset 函数是以字节为单位进行设置,

所以是将 arr 数组的前四个字节全部置 1 ,也就是第一个元素的四个字节全部置 1,最后的结果为 16843099。

4. memcmp 函数的使用

在这里插入图片描述

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  1. 比较从 ptr1ptr2 指针指向的位置开始,向后的num个字节

  2. 返回值如下:
    在这里插入图片描述
    函数使用示例:

#include <stdio.h>
#include <string.h>
int main()
{char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";int n;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;
}

关于 memcpy 函数在使用时我们要注意下面的代码:

int arr[2] = {1, 2};
int arr1[3] = {1, 2, 3};
memcpy(arr, arr1, 9)

当函数比较第 9 个字节时,由于数组 arr 只开辟了 8 字节大小的空间,所以这时我们并不知道在数组 arr 后一个字节存储的内容,

这时系统仍然会根据内存中的内容进行比较,所以要避免写出这样的代码。

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

相关文章:

  • IIS-网站报500.19错误代码0x8007000d问题解决
  • LeetCode Hot100【4. 寻找两个正序数组的中位数】
  • 什么是 WebClient?
  • xss-labs的小练
  • 基于faster-r-cnn行人检测和ResNet50+FPN的可见光红外图像多模态算法融合创新
  • VIVADO技巧_BUFGMUX时序优化
  • 比特币技术简史 第二章:密码学基础 - 哈希函数、公钥密码学与数字签名
  • 基于阿里云云服务器-局域网组网软件
  • Mfc初始化顺序
  • 【27】MFC入门到精通——MFC 修改用户界面登录IP IP Address Control
  • 虚幻引擎5 GAS开发俯视角RPG游戏 #06-7:无限游戏效果
  • 【28】MFC入门到精通——MFC串口 Combobox 控件实现串口号
  • 技术演进中的开发沉思-36 MFC系列: 对话框
  • Java并发编程(一)
  • LeetCode Hot 100 二叉树的最大深度
  • .NET 10 Preview 4 已发布
  • 【C# in .NET】9. 探秘委托:函数抽象的底层机制
  • 设置第三方窗口置顶(SetWindowPos方法,vb.net)
  • WMS仓储管理系统智能调控提升电子企业库存周转率
  • 系统启动流程分析
  • Linux-RAID
  • QML 五大对话框组件
  • 端口被占用时的解决问题
  • Egg.js × NestJS 2025 Nodejs后端框架选型指南
  • 代码随想录算法训练营十七天|二叉树part07
  • 【android bluetooth 协议分析 03】【蓝牙扫描详解 2】【app触发蓝牙扫描后,协议栈都做了那些事情】
  • 跨平台 App 如何无痛迁移到鸿蒙系统?全流程实战+Demo 教程
  • 八股文——包装类
  • Android 升级targetSdk无法启动服务
  • 动态规划题解——分割等和子集【LeetCode】