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

C语言中的内存函数

目录

  • 1 memcpy()函数的基本信息及功能
    • (1) void * destination
    • (2) const void * source
    • (3) size_t num
  • 1.2 memcpy()函数实战演练
  • 1.3 memcpy()函数的模拟实现
    • 1.3.1 my_memcpy()函数定义及参数
    • 1.3.2 my_memcpy()函数函数体
    • 1.3.3 my_memcpy()函数拷贝元素堆叠
  • 2 memmove()函数的基本信息及功能
    • 2.1 memmove()函数实战演练
    • 2.2 memmove()函数的模拟实现
      • 2.2.1 my_memmove()函数定义及参数
  • 3 memset()函数的基本信息及功能
    • (1)void * ptr
    • (2)int value
    • (3)size_t num
    • 3.1 memset()函数实战演练
    • 3.2 memset()函数能否把整型数组中的元素全部替换为1
  • 4 memcmp()函数的基本信息及功能
  • (1)const void * ptr1
  • (2)const void * ptr2
  • (3)size_t num
    • 4.1 memcmp()函数实战演练
      • 4.1.1 memcmp()不提前结束
      • 4.1.2 memcmp()提前结束

1 memcpy()函数的基本信息及功能

我们从cplusplus官网(cplusplus(C语言函数查询网站)上我们不难看到memcpy()函数是有三个参数如图:
memcpy函数拷贝结束后,会返回目标空间的起始地址类型是(void *)在这里插入图片描述

1、函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
2、该函数在遇到’\0’字符时不会停止执行。需要注意的是,与strcpy()函数不同,strcpy()专门用于字符串的复制操作,而memcpy()则适用于任意数据类型的复制。

下面我们对每个参数进行详细解释。

(1) void * destination

在这里插入图片描述
调用该函数时,需将目的地数组的地址作为第一个参数传入,该地址将由 void * 类型的指针destination 接收。需要注意的是,void * 类型的指针是一种无具体类型的指针,能够接受任何数据类型的地址,因此 memcpy() 函数可以实现任意数据类型之间的拷贝。

(2) const void * source

这里是引用
将源数组的地址作为第二个参数传入,该地址由 const void * 类型的指针 source 接收(const 位于 * 左侧,表示不能对指针进行解引用操作)。

(3) size_t num

1、要复制的字节数。
2、size_t 是无符号整型。

1.2 memcpy()函数实战演练

我们下面使用memcpy()函数进行一个整型数组的拷贝,两个数组如下:

	int arr1[] = { 1, 2, 3, 4, 5 , 6, 7, 8, 9, 10 };//源头int arr2[20] = { 0 };//目的地

接下来我们将调用标准库中的memcpy()函数,实现数组元素的复制作。具体操作是将arr1数组的前5个元素复制到arr2数组中。调用时需传入三个参数:
目标数组arr2、源数组arr1,以及要复制的字节数5*sizeof(int)。这里需要注意的是,字节数等于要复制的元素数量乘以每个元素所占的字节大小。

在这里插入图片描述

完整代码如下:

#include <stdio.h>
#include <string.h>
int main() {int arr1[] = { 1, 2, 3, 4, 5 , 6, 7, 8, 9, 10 };//源头int arr2[20] = { 0 };//目的地//目标将arr1数组中的前5个元素拷贝到arr2数组中//memcpy - 针对内存块进行拷贝//memcpy函数拷贝结束后,会返回目标空间的起始地址int* ret = memcpy(arr2, arr1, 5*sizeof(int));int i = 0;for (i = 0; i <= 19; i++) {printf("%d ", *(ret+i));}return 0;
}

运行结果:

在这里插入图片描述

1.3 memcpy()函数的模拟实现

掌握了memcpy()函数的基本用法后,接下来我们将尝试模拟实现它,编写一个自定义的my_memcpy()函数。

1.3.1 my_memcpy()函数定义及参数

首先,设计函数的三个参数, 因为要模拟实现 memcpy()函数,因此直接 仿照memcpy()函数的参数即可:

void* my_memcpy(void* dest, const void* src, size_t num)

1.3.2 my_memcpy()函数函数体

//memcpy()函数的模拟实现
void* my_memcpy(void* dest, const void* src, size_t num) {void* ret = dest;assert(dest && src);while (num--){*(char *)dest = *(char *)src;//指针加1的第一种写法//dest = (char*)dest + 1;//src = (char*)src + 1;指针加1的第二种写法((char* )dest)++;((char*)src)++;//(char *)dest++错误写法,dest指针只是临时转换为char * 指针并没有使用,本质还是void *类型指针,无法进行操作}return ret;
}

为了实现任意数据类型的拷贝,由于无法预知dest 和 src参数的具体类型,我们将其声明为 void * 类型。 void * 是一种无具体类型的指针,能够接收任何类型的地址。然而,由于 void * 指针无法直接解引用或进行整数加减运算,我们需要将其统一强制转换为 char * 类型。这种转换使得我们可以将内存视为按字节操作的空间,从而逐字节进行拷贝。

在拷贝过程中,我们需要处理 num 个元素。由于 char * 类型的指针每次解引用只能操作(拷贝)一个字节,因此外层需要使用 while 循环,循环次数为待拷贝的元素个数乘以每个元素的大小。

该部分完整代码如下:

#include <stdio.h>
#include <assert.h>
//memcpy()函数的模拟实现
void* my_memcpy(void* dest, const void* src, size_t num) {void* ret = dest;assert(dest && src);while (num--){*(char *)dest = *(char *)src;//指针加1的第一种写法//dest = (char*)dest + 1;//src = (char*)src + 1;指针加1的第二种写法((char* )dest)++;//(char *)dest++错误写法,dest指针只是临时转换为char * 指针并没有使用,本质还是void *类型指针,无法进行操作((char*)src)++;}return ret;
}
int main() {int arr1[] = { 1, 2, 3, 4, 5 , 6, 7, 8, 9, 10 };//源头int arr2[20] = { 0 };//目的地//my_memcpy(arr2, arr1, 20);//数组下标法int *ret = my_memcpy(arr2, arr1, 20);//指针法打印int i = 0;int size = 0;size = sizeof(arr2) / sizeof(arr2[0]);//计算数组的大小//打印方法(数组下标进行打印): //for ( i = 0; i < size; i++)//{//	printf("%d ", arr2[i]);//}//打印方法(用函数返回值——>指针进行打印)for (i = 0; i <= 19; i++) {printf("%d ", *(ret+i));}return 0;}

1.3.3 my_memcpy()函数拷贝元素堆叠

如果source和destination有任何的重叠,复制的结果都是未定义的。

//探究 ——> 如果source和destination有任何的重叠,复制的结果都是未定义的。
void* my_memcpy(void* dest, const void* src, size_t num)
{void* ret = dest;int i = 0;assert(dest && src);while (num--){*(char*)dest = *(char*)src;((char*)src)++;((char*)dest)++;}return ret;
}int main()
{int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };my_memcpy(arr1 + 2, arr1, 20);//arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }//预期效果: 1  2  1  2  3  4  5  8  9  10//实际效果: 1  2  1  2  1  2  1  8  9  10int i = 0;for ( i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

该情况如图

在这里插入图片描述

运行结果:

这里是引用

当源空间和目标空间出现重叠时,需要使用接下来介绍的memmove()函数。

2 memmove()函数的基本信息及功能

我们从cplusplus官网(cplusplus(C语言函数查询网站)上我们不难看到memmove()函数是有三个参数如图:
在这里插入图片描述

memmove() 函数的参数与memcpy() 函数 完全一致,两者的主要区别在于 memmove() 能够正确处理源内存块和目标内存块重叠的情况。

Tips:如果源空间和⽬标空间出现重叠,就得使用memmove函数处理。

2.1 memmove()函数实战演练

我们下面使用memmove()函数进行一个整型数组(源空间和⽬标空间出现重叠)的拷贝,如下:

//memcpy()函数实战演练
int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };memmove(arr + 2, arr, 5 * sizeof(int));int i = 0;for ( i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

这两个数组在拷贝时存在元素重叠的情况。

在这里插入图片描述

arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
预期效果:1  2   1  2  3  4  5  8  9  10
堆叠效果:1  2   1  2  1  2  1  8  9  10

这个时候,我们就要用到memcpy() 函数进行拷贝

运行结果:
在这里插入图片描述

2.2 memmove()函数的模拟实现

掌握了memmove()函数的基本用法后,接下来我们将尝试模拟实现它,编写一个自定义的my_memmove()。

2.2.1 my_memmove()函数定义及参数

首先,设计函数的三个参数, 因为要模拟实现memmove()函数,因此直接 仿照memmove()函数的参数即可:

void* my_memcpy(void* dest, const void* src, size_t num)

my_memmove()函数的实现思路与my_memcpy()函数基本一致,但增加了对内存重叠情况的处理。根据源地址与目标地址的相对位置,主要存在以下三种重叠情形:

情况一:src > dest(内存有重叠)

这里是引用

情况二:src < dest(内存有重叠)

这里是引用

情况三:src < dest(内存无重叠)

在这里插入图片描述

综上我们可以把上述三种情况总结为:

这里是引用

1、 当目标地址位于图中区域1时,采用从前向后的顺序进行拷贝;
2、当目标地址位于图中区域2时,采用从后向前的顺序进行拷贝;
3、当目标地址位于图中区域3时,可选择从前向后或从后向前的顺序进行拷贝。

因为,数组随着下标的增加地址是由低到高变化的。于是,我们判断重叠机制就有以下两种写法:

第一种写法:
若目标地址 dest 小于源地址 src,则从前向后执行拷贝操作;反之,则从后向前进行拷贝。

代码如下:

void* my_memcpy(void* dest, const void* src, size_t num) {void* ret = dest;assert(dest && src);if (dest < src)//从前往后进行拷贝{while (num--){*((char*)dest) = *((char*)src);dest = (char*)dest + 1;src = (char*)src + 1;}}else//从前往后进行拷贝{while (num--) {*((char*)dest + num) = *((char*)src + num);}}return ret;
}

第二种写法:
当 dest 地址位于 src 地址之前,或超过 src 加上拷贝元素个数的地址范围时,采用从前向后的拷贝方式;其余情况则一律从后向前进行拷贝。

代码如下:

//方法2
void* my_memcpy(void* dest, const void* src, size_t num) {void* ret = dest;assert(dest && src);if (dest < src || dest >= (char *)src + num)//从前往后{while(num--){*((char*)dest) = *((char*)src);dest = (char*)dest + 1;src = (char*)src + 1;}}else//从后往前{while (num--){*((char*)dest + num) = *((char*)src + num);}}return ret;
}

如何进行从后到前的拷贝呢?关键是要找到源数组和目标数组的最后一个,元素下面通过图示进行讲解。假设需要将源数组中的5个元素拷贝到目标数组中。

在这里插入图片描述

如图所示,当需要拷贝5个元素时,我们只需将src指针向后移动5 * sizeof(int) - 1次,即可定位到待拷贝元素的最后一个位置。同理,dest指针经过相同次数的移动后,也能指向目标位置的最后一个元素(5个元素中的最后一个元素)。

完整代码如下:

//方法一
void* my_memcpy(void* dest, const void* src, size_t num) {void* ret = dest;assert(dest && src);if (dest < src)//从前往后进行拷贝{while (num--){*((char*)dest) = *((char*)src);dest = (char*)dest + 1;src = (char*)src + 1;}}else//从前往后进行拷贝{while (num--) {*((char*)dest + num) = *((char*)src + num);}}return ret;
}//方法2
//void* my_memcpy(void* dest, const void* src, size_t num) {
//	void* ret = dest;
//	assert(dest && src);
//	if (dest < src || dest >= (char *)src + num)//从前往后
//	{
//		while(num--)
//		{
//			*((char*)dest) = *((char*)src);
//			dest = (char*)dest + 1;
//			src = (char*)src + 1;
//		}
//	}
//	else//从后往前
//	{
//		while (num--)
//		{
//			*((char*)dest + num) = *((char*)src + num);
//		}
//	}
//	return ret;
//}
//int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//使用自己的memcpy()函数my_memcpy(arr + 5, arr , 5 * sizeof(int));int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

3 memset()函数的基本信息及功能

memset()函数 是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。

我们从cplusplus官网(cplusplus(C语言函数查询网站)上我们不难看到memset()函数是有三个参数如图:
在这里插入图片描述

(1)void * ptr

这里是引用
调用该函数时,需将待填充内存块的地址作为首个参数传递给ptr指针。由于void *类型指针是一种通用指针,能够接收任意数据类型的地址,因此memset()函数能够以字节为单位,将任意数据类型的内存内容设置为指定值。

(2)int value

在这里插入图片描述

(3)size_t num

这里是引用

3.1 memset()函数实战演练

我们下面使用memset()函数进行一个应用,如下:

#include <stdio.h>
#include <string.h>
int main() {char arr[] = "hello world";memset(arr + 2, 'y', 7);printf("%s\n", arr);return 0;
}

该代码将从arr地址偏移2个单位的位置开始,将后续7个元素全部赋值为’y’。

运行结果

这里是引用

3.2 memset()函数能否把整型数组中的元素全部替换为1

目的:想要通过memset()函数将整型数组中的元素全部替换为1
代码如下:

//探讨memset()函数能否把整型数组中的元素全部替换为1
int main() {int arr[5] = { 1, 2, 3, 4, 5 };memset(arr, 1, 20);//以字节为单位设置的int i = 0;for ( i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;
}

要理解这个问题,首先需要明确数据是以二进制补码形式存储在内存中的。整型数据占用4个字节,即32个比特位。由于正数的原码、反码和补码完全一致,因此我们可以直接得出arr数组中各数字对应的补码表示。
在这里插入图片描述4个二进制数对应一个16进制数,下方为对应的16进制数。

由于内存单元以字节为单位,我们可以通过调试工具直观地观察数据在内存中的存储方式。

在这里插入图片描述

在这里插入图片描述

由于 memset() 函数以字节为单位对内存进行赋值操作,执行上述代码后,每个字节都将被设置为 1。

在这里插入图片描述

内存中:

在这里插入图片描述
我们观察到,确实每个字节的内容都被设置成了1

运行结果:
在这里插入图片描述

结论:memset()函数无法将整型数组中的所有元素统一设置为1,它仅能将每个字节替换为1。

4 memcmp()函数的基本信息及功能

memcmp()函数比较从ptr1和ptr2指针指向的位置开始,向后的num个字节。

我们从cplusplus官网(cplusplus(C语言函数查询网站)上我们不难看到memcmp()函数是有三个参数如图:
在这里插入图片描述

该函数的返回值:
(1)如果 * ptr1(即ptr1指向的数据)小于 * ptr2(即ptr2指向的数据),则返回一个小于0的整数(例如-1)。
(2)如果 * ptr1等于 * ptr2,则返回0。
(3)如果 * ptr1大于 * ptr2,则返回一个大于0的整数(例如1)。

(1)const void * ptr1

这里是引用
ptr1 是一个 const void * 类型的指针,它指向待比较的第一个内存块的起始地址。const 修饰符位于 * 号的左侧,这意味着 ptr1 所指向的内存内容是不可修改的,即通过 ptr1 无法对内存块中的数据进行任何写操作。

(2)const void * ptr2

在这里插入图片描述

与ptr1类似,ptr2指向第二个待比较内存块的起始地址。

(3)size_t num

在这里插入图片描述
比较 ptr1 和 ptr2 指向的内存区域时,需要向后比较 num 个字节。但在比较过程中,若提前发现大小差异,即可终止比较,否则需完整比较全部 num 个字节。

4.1 memcmp()函数实战演练

4.1.1 memcmp()不提前结束

该情况是memcmp()函数会逐字节比较数据,直到达到指定的num字节数为止。

#include <stdio.h>
#include <string.h>
int main() {int arr1[] = { 1, 2, 3, 4, 5, 6, 7 };int arr2[] = { 1, 2, 3, 4, 5, 8, 8 };int ret = memcmp(arr1, arr2, 20);printf("%d\n", ret);return 0;
}

在这里插入图片描述

运行结果
在这里插入图片描述

4.1.2 memcmp()提前结束

该情况是memcmp()函数会逐字节比较数据,但是未到达到指定的num字节数为止。这是因为在这之前就发现了大小关系!

#include <stdio.h>
#include <string.h>
int main() {int arr1[] = { 1, 2, 3, 4, 5, 6, 7 };int arr2[] = { 1, 2, 3, 4, 5, 8, 8 };	int ret = memcmp(arr1, arr2, 7 * sizeof(int));printf("%d\n", ret);return 0;
}

在这里插入图片描述

运行结果:
在这里插入图片描述

以上就是关于内存函数及其部分模拟实现的全部内容,希望能对大家有所帮助或有所启发,感谢各位小伙伴的耐心阅读。咱们下期见!拜拜~

在这里插入图片描述

http://www.xdnf.cn/news/568189.html

相关文章:

  • node.js如何实现双 Token + Cookie 存储 + 无感刷新机制
  • docker exec -it abc bash
  • 【深度学习】使用Anaconda和PyTorch在无显卡Windows系统上配置强化学习环境
  • 亚马逊第四个机器人中心将如何降低30%配送成本?
  • iOS 直播技术及优化
  • Mariadb cpu 93% 问题
  • Ubuntu22.04 系统安装Docker教程
  • 鸿蒙 模块的创建+Video简单使用
  • 在SpringBoot项目中,使用单元测试@Test
  • 解决dedecms织梦系统{dede:arclist keyword=‘动态获取关键词‘}只生效一次
  • Java虚拟机 -虚拟机栈
  • 名师在线杂志名师在线杂志社名师在线编辑部栏目设置
  • 制作一款打飞机游戏53:子弹样式
  • 【Qt】:设置hover属性,没有适应到子控件中
  • 工业相机图像采集卡:机器视觉的核心枢纽
  • 04算法学习_209.长度最小的子数组
  • OS进程调度
  • 第23天-Python Flet 开发指南
  • 多模态大语言模型arxiv论文略读(八十六)
  • LAN(局域网)和WAN(广域网)
  • 深入理解万维网:URL、HTTP与HTML
  • 电路设计基础
  • 前端JavaScript-嵌套事件
  • matlab加权核范数最小化图像去噪
  • Linux——PostgreSQL数据库日常维护
  • 25_05_19Linux实战篇、第一章_02若依前后端部署之路(前端)
  • 在 Excel xll 自动注册操作 中使用东方仙盟软件————仙盟创梦IDE
  • 代码随想录 算法训练 Day8:字符串part01
  • 关于TCP三次握手
  • 【ULR #1】打击复读 (SAM, DAG链剖分)