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

C语⾔内存函数

前言

本篇文章将讲解C语⾔内存函数知识的相关内容,为本章节知识的内容。

本篇文章涉及的C语⾔内存函数有:

1.memcpy函数

2.memmove函数

3.memset函数

4.memcmp函数

为本次讲解的知识。

一、 memcpy函数

1.介绍

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

  memcpy函数strcpy函数功能相似,不同的是memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。

注意:source 和 destination 有任何的重叠,复制的结果都是未定义的,更易懂的角度来说:

例:

int arr[5] = {1, 2, 3, 4, 5};
memcpy(arr+1, arr, 3*sizeof(int)); // 错误:目标区域 (arr+1) 与源区域 (arr) 重叠

memcpy不允许同名数组的复制。

destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。

source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。

num :从source要拷⻉的数据占据的字节数。

返回值:memcpy 函数返回的⽬标空间的起始地址。

使用例:

#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;
}

例子讲解:

本代码中,是将 arr1 的前20字节数据复制到 arr2 中,然后打印 arr2 的所有元素。

  • memcpy(arr2, arr1, 20) 表示复制 20字节 数据。由于每个 int 占4字节,因此实际复制 20 / 4 = 5 个元素。
  • 从 arr1 的起始地址复制5个元素:1, 2, 3, 4, 5
  • arr2 初始化为全0,因此未被复制的后5个元素保持为0。

所以可知打印时后5个值为0.

2.模拟实现:

#include <stdio.h>
#include <string.h>
void * mymemcpy ( void * dst, const void * src, int n )
{  void *ret=dst;
while(n--)
{
*(char*)dst=*(char*)src;
dst=(char*)dst+1;
src=(char*)src+1;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
mymemcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}

对该实现的讲解:

首先模拟实现要满足原函数的参数,返回值等,所以要满足void * memcpy ( void * destination, const void * source, size_t num );的型状。

因为返回值:memcpy 函数返回的⽬标空间的起始地址。

所以用void *ret=dst;语句来接⽬标空间的起始地址。

memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,拷贝的是num个字节,但不同类对应着不同的字节数,所以存在拷贝不完全的情况,而char类型1个字节数,可以完成1个1个复制拷贝的情况,所以   *(char*)dst=*(char*)src; 拷贝完一个字符,就该向下拷贝,但二者均为void *类,所以应该通过强转之后移动地址,所以会有  dst=(char*)dst+1;
src=(char*)src+1; 语句,在结尾,完成返回值:memcpy 函数返回的⽬标空间的起始地址。 return ret;

  运行结果一样的,看实现代码,我们可以发现,memcpy不能实现同名数组的复制,如果我们想实现,则可以靠二、memmove函数

  二、memmove函数

1.介绍

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

memmove函数也是完成内存块拷⻉的。

他与 memcpy函数功能一样,是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。不过mommove可以正确处理目标空间与源空间重叠的情况。

源内存块和⽬标内存块是可以重叠的。

二者的参数相同,意义也一样的:

destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。

source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。

num :从source要拷⻉的数据占据的字节数。

返回值: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;
}

讲解:

与在memcpy中的使用方法一样,不同的是该例为同名数组的拷贝,即将该数组的前5个值拷贝从(arr1 + 2)即arr[2]开始。

2.模拟实现:

#include <stdio.h>
#include <string.h>
void * mymemmove ( void * dst, const void * src, int n )
{
void *ret=dst;
if(dst<src)
{
while(n--)
{
*(char*)dst=*(char*)src;
dst=(char*)dst+1;
src=(char*)src+1;
}
}
else
{
while(n--)
*((char*)dst+n)=*((char*)src+n);
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
mymemmove(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}

  运行结果一样,接下来对该实现讲解一下:

核心逻辑分析

mymemmove 通过判断目标地址(dst)和源地址(src)的位置关系,选择不同复制方向:

  • 当 dst < src(目标在源左侧):从低地址到高地址复制(避免覆盖未复制的源数据)。(对应图中的一情况)
  • 当 dst >= src(目标在源右侧或重叠):从高地址到低地址复制(从 n-1 反向遍历)。(对应图中的二情况)

输入参数

  • arr1 初始值:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • mymemmove(arr1 + 2, arr1, 20)
    • dst = arr1 + 2(目标起始地址:第3个元素)
    • src = arr1(源起始地址:第1个元素)
    • n = 20(复制20字节,int 占4字节 → 共复制 5个元素

关键判断

dst (arr1+2) > src (arr1) → 进入 else 分支(从高地址到低地址复制)。

在 else中拷贝。

    while(n--)
*((char*)dst+n)=*((char*)src+n);

最后: return ret;完成返回值: memmove函数返回的⽬标空间的起始地址。

 三、memset函数

1.介绍

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

1.memset 函数是设置内存块的内容的,将内存中指定⻓度的空间设置为特定的内容。 2.memset 的使⽤需要包含<string.h>。

参数讲解:

1.ptr :指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址。

2.value :要设置的值,函数将会把value值转换成unsigned char的数据进⾏设置的。也就是以字节为单位来设置内存块的。

3.num :要设置的内存⻓度,单位是字节。

4.返回值:返回的是要设置的内存空间的起始地址。

  使用例:

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

对于该例的解释:

  1. 初始字符串char str[] = "hello world" 定义了一个字符数组,存储字符串 "hello world"(共11个字符 + 1个结束符 \0,总长度12字节)。

  2. memset 操作

    • 作用:将 str 起始的 6个字节 全部设置为字符 'x'
    • 原字符串前6个字符是 "hello "(注意第6个字符是空格),替换后变为 "xxxxxx"
  3. 剩余字符不变

    • memset 仅修改前6字节,原字符串从第7字节开始的内容("world\0")保持不变。
    • 拼接后结果为 "xxxxxxrld\0"(第7个字符是 'r',因此输出 xxxxxxrld)。

所以是这样的结果。

注意:当有⼀块内存空间需要设置内容的时候,就可以使⽤memset函数,值得注意的是memset函数对内存 单元的设置是以字节为单位的。

2.模拟实现:

#include <stdio.h>
#include <string.h>
void * mymemset ( void * p, int value,int n)
{  void *ret=p;
while(n--)
{
*(char*)p=value;
p=(char*)p+1;
}
return ret;
}
int main ()
{
char str[] = "hello world";
mymemset (str,'x',6);
printf(str);
return 0;
}

讲解:

  • mymemset执行流程
    • 保存原始指针ret = p,用于最终返回。
    • 循环6次(n--从6到0),每次将p指向的字节设为'x',并将p按字节递增((char*)p + 1)。
    • 原字符串前6字节("hello ")被替换为"xxxxxx",后续字符"world\0"保持不变,拼接后结果为"xxxxxxrld\0"
  • printf输出:打印字符串至结束符\0,结果为 xxxxxxrld

 四、memcmp函数

1.介绍

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

功能:

1.⽐较指定的两块内存块的内容,⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节 2.memcmp 的使⽤需要包含

ptr1 :指针,指向⼀块待⽐较的内存块

ptr2 :指针,指向另外⼀块待⽐较的内存块

num :指定的⽐较⻓度,单位是字节

  使用例:

#include <stdio.h>
#include <string.h>
int main()
{
char s1[] = "abcdefg";
char s2[] = "abcdEFG";
int n;
n = memcmp(s1, s2, sizeof(s1));
if (n > 0)
printf("'%s'>'%s'.\n", s1, s2);
else if (n < 0)
printf("'%s' <'%s'.\n",s1,s2);
else
printf("'%s'  =='%s'  .\n", s1, s2);
return 0;
}

讲解:

  • 输出内容'abcdefg' < 'abcdEFG'.
  • 关键原因memcmp字节逐一比较(区分大小写),在第5个字符处:
    • s1[4] = 'e'(ASCII值101)
    • s2[4] = 'E'(ASCII值69)
      因 101 > 69memcmp返回正数,n>0, 执行: if (n > 0)       printf("'%s'>'%s'.\n", s1, s2);

注意:1.memcmp 函数是通过返回值告知⼤⼩关系的。

           2.如果要⽐较2块内存单元的数据的⼤⼩,可以使⽤该函数,这个函数的特点就是可以指定⽐较长度。

2.模拟实现:

#include <stdio.h>
#include <string.h>
int mymemcmp ( const void * p1, const void * p2, int n )
{
const char* s1 = (const char*)p1;
const char* s2 = (const char*)p2;
while (n-- > 0)
{
if (*s1 != *s2)
{
return *s1 - *s2;
}
s1++;
s2++;
}
return 0;
}
int main()
{
char s1[] = "abcdefg";
char s2[] = "abcdEFG";
int n;
n = mymemcmp(s1, s2, sizeof(s1));
if (n > 0)
printf("'%s'>'%s'.\n", s1, s2);
else if (n < 0)
printf("'%s' <'%s'.\n",s1,s2);
else
printf("'%s'  =='%s'  .\n", s1, s2);
return 0;
}

结果一样,接下来,我将为大家讲解一下:

因为参数为void*类型,如果使用,需要     *(char*)p1  这种的强转,代码中每次比较和移动指针时都进行(char*)强制转换,可通过临时变量优化,const char* s1 = (const char*)p1;
const char* s2 = (const char*)p2;通过char*类型,便于比大小和向下移动。

    while (n-- > 0)
{
if (*s1 != *s2)
{
return *s1 - *s2;
}
s1++;
s2++;
}
return 0;

  • 循环条件n-- > 0 表示从第 1 个字节开始,逐个比较 n 个字节(每轮循环后 n 减 1,直到 n=0 退出)。
  • 逻辑
    • 每次循环读取 s1 和 s2 指向的当前字节(*s1/*s2)。
    • 若字节不同,直接返回两字节的 ASCII 码差值(例如 'e' - 'E' = 101 - 69 = 32,结果为正数)。
    • 若字节相同,指针后移(s1++/s2++),继续比较下一个字节。
    • 若循环结束后所有 n 字节均相同(未触发 return *s1 - *s2),则显式返回 0。

总结

以上就是今天要讲的内容,

本篇文章涉及的C语⾔内存函数有:

1.memcpy函数

2.memmove函数

3.memset函数

4.memcmp函数

知识的相关内容,为本章节知识的内容,希望大家能喜欢我的文章,谢谢各位。


文章转载自:

http://WWE03SPW.wqpsf.cn
http://lS6ZP6kD.wqpsf.cn
http://rhjTWB4N.wqpsf.cn
http://Vh1JPQUZ.wqpsf.cn
http://KvckBo7k.wqpsf.cn
http://Bd2LJMda.wqpsf.cn
http://EwklNtYq.wqpsf.cn
http://Tfg5Jat8.wqpsf.cn
http://4oeePFlR.wqpsf.cn
http://AlCLz3Mn.wqpsf.cn
http://XumlRorO.wqpsf.cn
http://041fisLA.wqpsf.cn
http://mlfpUjKW.wqpsf.cn
http://E7VcoDao.wqpsf.cn
http://VSVNV2AQ.wqpsf.cn
http://CPiyFb3i.wqpsf.cn
http://VyOt2k0L.wqpsf.cn
http://m7Y7fnX4.wqpsf.cn
http://VuZlWOTc.wqpsf.cn
http://mHGbUMUs.wqpsf.cn
http://NqxrQnmX.wqpsf.cn
http://sh2oJZqJ.wqpsf.cn
http://7KxD5XP9.wqpsf.cn
http://KnXAevhy.wqpsf.cn
http://BhLljHuU.wqpsf.cn
http://uF0R14hL.wqpsf.cn
http://QYXFAUfD.wqpsf.cn
http://HEyXuKiG.wqpsf.cn
http://XPJN3aMa.wqpsf.cn
http://xUJB1IJF.wqpsf.cn
http://www.dtcms.com/a/376782.html

相关文章:

  • go资深之路笔记(一) Context
  • 数学建模资源合集
  • STM32项目分享:基于STM32智能吸尘器系统的设计与实现
  • 计算机毕设 java 高校会议室预约管理系统 基于 SSM 框架的高校会议室管理平台 Java+MySQL 的预约全流程管控系统
  • vue-pdf 实现blob数据的预览
  • RiskBird企业信息模糊查询工具
  • 常用PDF转换工具推荐
  • ES6 类与继承:现代 JavaScript 面向对象编程
  • 使用 Docker Buildx 制作并推送双架构镜像
  • PDF Reader 编辑阅读(Mac)
  • springboot响应式编程笔记
  • 论文阅读:ACL 2024 Stealthy Attack on Large Language Model based Recommendation
  • WebView电视v1.13.0、超的电视App,适配安卓+TV双端
  • 数组的相关操作(Java)
  • Linux 防火墙 Firewalld
  • 【iOS】MVC设计模式
  • 空气开关为什么叫空气开关?
  • win11 idea图标在任务栏中展示为空白
  • GaussDB 中 alter default privileges 的使用示例(下)
  • 自建注册中心
  • PMP考试结构、学习框架与基本术语
  • BrotliCompressor压缩器封装,以及 PDF编码器介绍
  • React 核心 Hook 与冷门技巧:useReducer、useEffect、useRef 及 is 属性全解析
  • 【大前端】 断点续传 + 分片上传(大文件上传优化) 的前端示例
  • 学习React-11-useDeferredValue
  • Django项目部署实战:Nginx + Gunicorn 云服务器完整指南
  • 【实战+原理】微软云 Azure Database 私有网络接入模式全解析:从子网委派到Private Endpoint
  • 排序---插入排序(Insertion Sort)
  • k8s的service
  • 量化冗余分析中变量的关系丨TomatoSCI分析日记