C语言——内存函数

引言

在之前的两篇文章中,我们学习了字符函数和字符串函数,C语言中还有一类库函数叫做内存函数

我们接下来去学习一下这类函数

内存函数

内存函数的功能和某些字符函数的功能相似,它们是通过访问地址的方式操作对象,可应用于任何类型的对象。它们的使用需要包含头文件<string.h>

memcpy

1.memcpy的用法

memcpy用于内存拷贝。它的主要作用是将指定源地址处的内容复制到指定的目标地址处,即将一段内存中的数据拷贝到另一段内存中。

函数原型为:

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

其中,其参数的含义如下:

destination:指向目标内存区域的指针,即要接收源内存内容的位置

source:指向源内存区域的指针,即要复制的内容所在的起始位置

num:要拷贝的字节数

返回值:

返回一个指向目标内存区域destination的指针

功能:

函数memcpy从source的位置开始向后赋值num个字节的数据到destination指向的内存位置

注意:

memcpy不会对目标内存进行初始化或清除,只是简单地覆盖指定大小的字节

如果源地址和目标地址重叠,则memcpy的行为是未定义的。在这种情况下,应该使用memmove函数

使用memcpy时,必须确保目标内存区域destination有足够的空间来容纳要复制的n个字节,否则会导致缓冲区溢出,引发程序错误或安全漏洞

2.memcpy的使用

#include<stdio.h>  
#include<string.h>

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

输出结果为:

0 1 2 3 4 0 0 0 0 0

3.memcpy的模拟实现

思路:与strncpy类似,只不过memcpy是将指定长度的内存内容到目标地址,并返回目标地址的起始位置。

代码实现如下:

void* my_memcpy(void* dest,const void* src, int n)
{
	void* ret = dest;
	assert(dest && src);
	//循环复制内存内容,直到复制了 n 个字节
	while (n--)
	{
		// 将源地址的当前字节复制到目标地址的当前位置 
		*(char*)dest = *(char*)src;		
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	int arr1[] = { 0,1,2,3,4,5,6,7,8,9 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

memmove

1.memmove的用法

memmove用于在内存中移动(复制)字节块。它的主要用途是处理源和目标内存区域重叠的情况,这是 memcpy 函数无法处理的

函数原型为:

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

其中,其参数的含义如下:

destination:指向目标内存区域的指针,即要接收源内存内容的位置

source:指向源内存区域的指针,即要复制的内容所在的起始位置

num:要拷贝的字节数

返回值:

memmove 函数返回指向目标内存区域的指针,即 destination

注意:

使用 memmove 时,即使源和目标内存区域重叠,它也能正确复制数据。这是因为 memmove 可能会采用不同的策略来确保数据的一致性和正确性,比如先临时存储源数据,然后再复制到目标位置

2.memmove的使用

int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	memmove(arr + 2, arr, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

输出结果为:

0 1 0 1 2 3 4 7 8 9

解释:

memmove 将数组的前5个整数(0, 1, 2, 3, 4)复制到了从第三个位置开始的内存

int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	memmove(arr, arr + 2, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

输出结果为:

2 3 4 5 6 5 6 7 8 9

解释:

由于memmove将数组从第三个元素开始的五个整数(2, 3, 4, 5, 6)复制到了数组的开始位置

3.memmove的模拟实现

我们现在已经大概理解了memmove的用法,现在我们来试着模拟实现memmove

我们先来分析一下:

情况1:

    int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	memmove(arr + 2, arr, 20);

从2的位置开始拷贝:2->0  3->1  4->2  5->3  6->4 拷贝成功

从6的位置开始拷贝:6->4  5->3  4->2  3->1  2->0 拷贝失败

情况2:

	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	memmove(arr, arr + 2, 20);

从0的位置开始拷贝:0->2  1->3  2->4  3->5  4->6 拷贝失败

从4的位置开始拷贝:4->6  3->5  2->4  1->3  0->2 拷贝成功

情况3:

	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	memmove(arr, arr + 5, 20);

从5的位置开始拷贝:5->0  6->1  7->2  8->3  9->4 拷贝成功

从9的位置开始拷贝:9->4  8->3  7->2  6->1  5->0 拷贝成功

情况4:

	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	memmove(arr + 5, arr, 20);

从0的位置开始拷贝:0->5  1->6  2->7  3->8  4->9 拷贝成功

从4的位置开始拷贝:4->9  3->8  2->7  1->6  0->5 拷贝成功

我们可以根据这些情况得出结论:

如果dest在src左边,就从首元素开始拷贝;如果dest在src右边,就从末尾元素开始拷贝

代码实现如下:

void* my_memmove(void* dest, void* src, int num)
{
    void* ret = dest;  
    assert(dest && src); 

    //判断目标地址是否小于或等于源地址  
    //如果是,则按照从前往后的顺序复制,防止覆盖还未复制的源数据  
    if (dest <= src)
    {
        //从前往后复制数据  
        while (num--) //循环num次,每次复制一个字节  
        {
            *(char*)dest = *(char*)src; //将src指向的字节复制到dest指向的位置  
            dest = (char*)dest + 1; // 将dest指针向后移动一个字节  
            src = (char*)src + 1; // 将src指针向后移动一个字节  
        }
    }
    else
    {
        // 如果目标地址大于源地址,则按照从后往前的顺序复制  
        // 这样可以避免在复制过程中覆盖还未读取的源数据  
        dest = (char*)dest + num - 1; // 将dest指针移动到目标区域的最后一个要复制的字节  
        src = (char*)src + num - 1; // 将src指针移动到源区域的最后一个要复制的字节  

        while (num--) 
        {
            *(char*)dest = *(char*)src; //将src指向的字节复制到dest指向的位置  
            dest = (char*)dest - 1; //将dest指针向前移动一个字节  
            src = (char*)src - 1; //将src指针向前移动一个字节  
        }
    }

    return ret; 
}
int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	my_memmove(arr + 2, arr, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

memset

1.memset的用法

memset用于将某一块内存中的内容全部设置为指定的值

函数原型为:

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

其中,其参数的含义如下:

ptr:指向要设置的内存区域的指针

value:要设置的值,该值被转换为 unsigned char 并复制到目标内存区域

num:要设置的字节数

返回值

返回指向被设置内存区域的起始位置的指针

注意:

memset 是按字节操作的,因此当你对非字符类型(如整数、浮点数等)的数组使用 memset 时,需要特别小心。确保你了解目标数组的类型,并知道如何正确地初始化它。对于非零初始化,特别是非字符类型,memset 可能不会按预期工作。

2.memset的使用

int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 5); //将前5个字符替换为'x'
	printf("%s\n", arr); //打印修改后的字符串
	return 0;
}

输出结果为:

xxxxx world

错误演示:

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	memset(arr, 1, sizeof(arr));
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

输出结果为:

16843009 16843009 16843009 16843009 16843009

memset设置是以字节为单位,容易造成不符合我们预期的情况

3.memset的模拟实现

思路:memset的模拟实现和strncpy有点相似,但memset需要将数据类型强制转换为(char*)

代码如下:

void* my_memset(void* str, int c, size_t n)
{
    assert(str);

    // 将void*类型的指针转换为char*类型的指针,以便按字节访问  
    char* tmp = (char*)str;

    // 使用while循环遍历n个字节  
    while (n--)
    {
        // 将当前字节设置为字符c  
        *tmp = (char)c;
        // 移动到下一个字节  
        tmp++;
    }
    return str;
}

int main()
{
    char str[] = "hello world";
    my_memset(str, 'x', 6);
    printf("%s\n", str);

    return 0;
}

memcmp

1.memcmp的用法

memcmp用于比较内存区域的内容

函数原型为:

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

其中,其参数的含义如下:

s1 和 s2 是指向要比较的内存区域的指针

n 是要比较的字节数

返回值:

返回值:

如果返回值 < 0,则表示 ptr1 小于 ptr2

如果返回值 > 0,则表示 ptr1 大于 ptr2

如果返回值 = 0,则表示 ptr1 等于 ptr2

注意:

memcmp 函数按字节比较两个内存区域的内容,而不是按数据类型或元素进行比较。因此,它对于比较任意类型的内存区域都是有效的,只要你知道要比较的字节数

2.memcmp的使用

int main()
{
	int arr1[] = { 0,1,2,3,4,5,5,5 };
	int arr2[] = { 0,1,2,3,4,6,6,6 };

	int ret = memcmp(arr1, arr2, 21);
	printf("%d\n", ret);
	return 0;
}

输出结果为:

-1

3.memcmp的模拟实现

思路:memcmp的模拟实现与strncmp差不多,只是也需要先强制类型转换

代码如下:

int my_memcmp(const void* str1, const void* str2, size_t n)
{
	assert(str1 && str2);
	char* p1 = (char*)str1;
	char* p2 = (char*)str2;
	while (n-- && (*p1 == *p2))
	{
		p1++;
		p2++;
	}
	return *p1 - *p2;
}

int main()
{
	int arr1[] = { 0,1,2,3,4,3,6,7 };
	int arr2[] = { 0,1,2,3,4,2,4,4 };

	int ret = my_memcmp(arr1, arr2, 24);
	printf("%d\n", ret);

	char str1[] = "abcddd";
	char str2[] = "abcdef";
	ret = my_memcmp(str1, str2, 5);
	printf("%d\n", ret);
	return 0;
}

输出结果为:

1
-1

结束语

希望看完这篇文章能对友友们有所帮助!!!

点赞收藏关注!!!

谢谢各位!!!

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-04 12:02:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-04 12:02:02       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-04 12:02:02       82 阅读
  4. Python语言-面向对象

    2024-04-04 12:02:02       91 阅读

热门阅读

  1. os模块篇(十二)

    2024-04-04 12:02:02       32 阅读
  2. 5.108 BCC工具之virtiostat.py解读

    2024-04-04 12:02:02       36 阅读
  3. .net 实现的 Webscoket 对象的一些细节和疑问

    2024-04-04 12:02:02       41 阅读
  4. Ideal Holidays

    2024-04-04 12:02:02       38 阅读
  5. Sora文本生成视频(附免费的专属提示词)

    2024-04-04 12:02:02       41 阅读
  6. PyTorch示例——使用Transformer写古诗

    2024-04-04 12:02:02       31 阅读