0基础C语言积跬步之内存函数

目录

简单介绍

[一、memcpy 使用和模拟实现](#一、memcpy 使用和模拟实现)

[(1)memcpy 的使用](#(1)memcpy 的使用)

[(2)memcpy 的模拟实现](#(2)memcpy 的模拟实现)

[二、memmove 使用和模拟实现](#二、memmove 使用和模拟实现)

[(1)memmove 的使用](#(1)memmove 的使用)

[(2)memmove 的模拟实现](#(2)memmove 的模拟实现)

[三、memset 函数的使用](#三、memset 函数的使用)

[四、memcmp 函数的使用](#四、memcmp 函数的使用)


简单介绍

mem的全拼是memory,在计算机里专门翻译叫:**内存、存储器,**我们以mem开头的函数,都是直接对内存操作的函数,我们称之为内存函数,和str开头的函数不同,str开头的函数只能处理字符串,但mem开头的函数可以用于任何类型的数据,下面让我们来看看有哪些内存函数

我们主要有下面这四大C语言内存函数:

  • memcpy:内存拷贝(不支持内存重叠)
  • memmove:内存拷贝(支持内存重叠)
  • memset:内存初始化(清零最常用)
  • memcmp:内存逐字节比较

总结:

  • str 系列函数 :参数是 char*只能处理 char 类型的字符串 ,以 \0 作为结束标志,不能处理 int、结构体等。
  • mem 系列函数 :参数是 void*不区分数据类型,按字节操作内存,char、int、数组、结构体都能用。
  • memcpy、memmove、memset、memcmp 这 4 个内存函数,全部都在 <string.h>

一、memcpy 使用和模拟实现

(1)memcpy 的使用

memcpy函数的原型与作用:

cpp 复制代码
void* memcpy(void* destination, const void* source, size_t num);
  • 函数 memcpy 从 source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果 source 和 destination 有任何的重叠,复制的结果都是未定义的。
  • 需要返回destination的起始地址!!!
  • 使用需要头文件#include <string.h>
cpp 复制代码
#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]);
	}
	printf("\n");
	return 0;
}

这串代码中memcpy函数是将arr1中的前二十个字节的内存,直接整块复制给了arr2,二十个字节也就是五个整型,将arr1的前五个整型复制给了arr2,arr2 剩下的 5 个元素保持初始值0,所以会打印1 2 3 4 5 0 0 0 0 0

运行截图:

对于自己复制自己这种,内存有重叠的数据,我们用memmove来处理

(2)memcpy 的模拟实现

cpp 复制代码
#include <stdio.h>
#include <assert.h>
//memcpy的模拟实现
void* my_memcpy(void* des, const void* src, size_t num)
{
	assert(des && src);
	void* start = des;
	int count = num;
	while (count--)
	{
		*((char*)des) = *((char*)src);
		((char*)des)++;
		((char*)src)++;
	}
	return start;
}
//主函数
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
	return 0;
}

在对于memcpy函数的模拟实现中,我们首先进行了一个assert断言,保证我们的目标和要复制的空间都不是空指针,接着我们创建了一个void* start指针,用于存放任意类型的地址,将des的地址存给我们的strat指针,也用于最后的返回值,因为我们C语言规定,memcpy函数需要返回destination的起始地址,接着我们创建了一个count整型变量,用于对我们复制的字节数进行计数,复制一个字节,count就减1,在我们的while循环中,我们采取一个字节一个字节复制,所以我们将des和src都强制类型转化为char*类型,解引用时刚好访问一个字节,复制完一个字节后,des++,src也++,但只能一个字节一个字节的++,所以也需要强制类型转化为char*类型,然后接着下次复制,直到循环结束,复制完成。

运行截图:

二、memmove 使用和模拟实现

(1)memmove 的使用

memmove函数的原型与作用:

cpp 复制代码
void* memmove(void* destination, const void* source, size_t num);
  • 和 memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。
  • mommove需返回目标空间destination的起始地址
  • 使用需要头文件#include <string.h>
cpp 复制代码
#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]);
    }
    printf("\n");
    return 0;
}

这串代码我们复制arr1的前20个字节的内存,也就是前五个整型元素1 2 3 4 5,从我们的arr1+2的地址处开始覆盖,也就是从arr1的第三个元素开始覆盖,结果也就是1 2 1 2 3 4 5 8 9 10

运行截图:

(2)memmove 的模拟实现

cpp 复制代码
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dst, const void* src, size_t num)
{
    void* start = dst;
    assert(dst && src);
    if (dst < src)
    {
        while (num--)
        {
            *((char*)dst) = *((char*)src);
            dst = (char*)dst + 1;
            src = (char*)src + 1;
        }
    }
    else
    {
        while (num--)
        {
            *((char*)dst + num) = *((char*)src + num);
        }
    }
    return start;
}
int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    my_memmove(arr1+2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);
    }
    printf("\n");
    return 0;
}

当dst的地址小于src的地址时,我们采取从src的起始地址开始以一个字节的大小从det的起始地址开始复制,也就是从前往后复制,代码和memcpy的复制方式一样,不过多赘述

当dst的地址大于src的地址时,我们采取从src的最后一个字节的地址开始以一个字节的大小从dst的最后一个字节地址开始复制,也就是从后往前复制,(char*)dst + num和(char*)src + num就能找到dst和src的最后一个字节的地址,随着num--,它们的地址刚好也一个字节一个字节往前挪,从后到前逐个字节进行复制

因为mommove需返回目标空间destination的起始地址,所以我们创建了void*类型的指针变量start

运行截图:

三、memset 函数的使用

memset函数的原型与作用:

cpp 复制代码
void* memset(void* ptr, int value, size_t num);
  • 将 ptr指向的内存块的前 num 个字节,逐个字节设置为指定的value值
  • 使用需要头文件#include <string.h>
cpp 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "hello world";
	memset(str, 'x', 5);
	printf("%s", str);
	printf("\n");
	return 0;
}

将str的前五个字节的值,设置成字符'x',因为这是char类型的数组,所以我们修改的就是前五个字符,结果也就是xxxxx world

运行截图:

那如果是改变整型数组的值,是否还能正确修改呢?我们来看看:

cpp 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	int arr[5] = { 0 };
	memset(arr,1,20);
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

我们知道一个int类型占4个字节,这里我们数组大小为5,也就是有5个int类型的元素,总共大小为20个字节,所以我们在memset中选择将前20个字节都修改成1,希望能将数组中的元素都变成1,但真实情况真的是这样子的吗?

我们来看看运行截图:

可以看到,并没有出现我们想要的结果,为什么会这些这种结果,我们来调试内存看看:

上面这个图就是我们数组所开辟的二十个字节的内存空间,可以看到,当我们将前20个字节直接都设置为1时,在内存中存储的每一个字节都改为了1,这样我们随便拿出一个arr[i],里面存的都是0x01010101,换算成十进制就是16843009,所以才会出现上面的那种效果

四、memcmp 函数的使用

memcmp函数的原型与作用:

cpp 复制代码
int memcmp(const void* ptr1, const void* ptr2, size_t num);
  • 比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
  • 以字节为单位进行比较,不关注数据类型
  • 使用需要头文件#include <string.h>
返回值 含义
0 前 num 个字节 完全相同
> 0 ptr1 大于 ptr2
< 0 ptr1 小于 ptr2
cpp 复制代码
#include <string.h>
#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,7 };
	int arr2[] = { 1,2,3,4,5 };
	int n = memcmp(arr1, arr2, 17);
	printf("%d\n", n);
	return 0;
}

我们比较arr1和arr2两个数组,如上述代码所示,当我们比较前十七个字节时,我们的返回值会是什么?我们来看看运行截图:

为什么会是1呢,我们来调试一下看看:

这是arr1中在内存中存储的数据

这是arr2中在内存中存储的数据

可以看到,前十六个字节都是相等的,当我们比较到第十八个字节时,arr1里存的是0x07,arr2里存的是0x05,所以arr1>arr2,它们的差值是2,然后因为VS 里的 memcmp 只会返回:-1、0、1,不会返回真实字节差值,所以我们会返回一个1,打印出n的结果就是1

感谢大家的观看,新人求互三,关注我必回关!下章见!

相关推荐
吃好睡好便好2 小时前
在Matlab中绘制杆状图
开发语言·学习·算法·matlab·信息可视化
Chen_harmony2 小时前
【习题04】计算求和
c语言
桀人2 小时前
C++——内存管理——new和delete的超详细解析
开发语言·c++
Shadow(⊙o⊙)2 小时前
Shell进程替换,自定义Shell解释器——字符串库函数灵活操作!
linux·运维·服务器·开发语言·c++·学习
数智工坊2 小时前
PyCharm 运行 Python 脚本总自动进 Test 模式?附 RT-DETRv2 依赖缺失终极排坑
开发语言·ide·人工智能·python·pycharm
再写一行代码就下班2 小时前
根据给定word模板,动态填充指定内容,并输出为新的word文档。(${aa}占位符方式且支持循环动态表格)
java·开发语言
七夜zippoe2 小时前
DolphinDB流数据表:创建与订阅
开发语言·订阅··dolphindb·数据表
彦为君2 小时前
JavaSE-05-字符串(全面深入)
java·开发语言·python·ai·ai编程
charlie1145141913 小时前
现代C++特性指南(4)——完美转发与移动语义实战
开发语言·c++·现代c++