C语言内存函数超详解

fhvyxyci 2024-08-24 14:35:02 阅读 83

文章目录

前言1. memcpy1. 1 memcpy 的使用1. 2 memcpy 的模拟实现

2. memmove2. 1 memmove 使用

3. memset3. 1 memset 函数的使用3. 2 memset 的模拟实现

4. memcmp4. 1 memcmp 函数的使用4. 2 memcmp 的模拟实现


前言

C语言为我们提供了字符串的一些函数,比如复制,比较等等,但是这些函数只能用在字符串上,而C语言的数据类型显然不止字符串一种,那应该怎么办?难道要把每一种数据类型都包装几个函数供我们使用吗?显然这太过臃肿。

为了解决这个问题,C语言在<code>string.h库中提供了内存函数供我们使用。

1. memcpy

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

函数memcpysource的位置开始向后复制num字节的数据到destination指向的内存位置。这个函数在遇到'\0'的时候并不会停下来。如果sourcedestination有任何的重叠,复制的结果都是未定义的。也就是说,sourcedestination后面的num个字节之间不能有重叠的部分

1. 1 memcpy 的使用

#include<stdio.h>

#include<string.h>

int main()

{ -- -->

int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };

int arr2[10] = { 0 };

memcpy(arr2, arr1, sizeof(arr1));//sizeof(arr)是40个字节,也就是10个 int 类型的变量

for (int i = 0; i < 10; i++)

printf("%d ", arr2[i]);

return 0;

}

memcpy函数操作的对象是内存中的数据,因此,无论是什么类型的数据,只要大小给的正确,都可以进行复制。

1. 2 memcpy 的模拟实现

在开始前,我们再来看一眼 memcpy 的声明:

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

为什么这里要用 void* 指针?显然是为了让这个函数能接受任何类型的指针,但在函数体内部,这样的指针是不能解引用的,那该怎么办?

我们知道,memcpy函数是以字节为单位复制数据的,而 char 类型的数据的大小就是一个字节,那么我们只需要在函数体里将两个形参都强制类型转换为char* 类型,就可以完成操作了。

void* my_memcpy(void* des, const void* source, size_t num)

{

//尽管说 memcpy 不能处理重叠的数据,但在模拟实现也不需要管数据是否重叠,因为这是未定义的

char* cur1 = (char*)des;//当然,你也可以省略这两步,在下面的循环中将两个形参进行临时转换

char* cur2 = (char*)source;

while (num--)

*cur1++ = *cur2++;

return des;//注意 memcpy 的返回类型是void* ,不是 void ,它返回的是目标内存的起始位置

}

2. memmove

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

memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。

2. 1 memmove 使用

既然 memmove 的特点就是能够处理重叠的数据,那我们就让它处理重叠的数据看看效果。

#include<stdio.h>

#include<string.h>

int main()

{

int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };

memmove(arr+1, arr, 9 * sizeof(int));

for (int i = 0; i < 10; i++)

{

printf("%d ", arr[i]);

}

return 0;

}

输出结果:

输出结果

我们来简单的分析一下

<code>memmove(arr+1, arr, 9 * sizeof(int));

这段代码是怎么工作的。

arr+1 是目标位置, arr 是源位置,arr 向后复制 9 个 int类型的大小到arr+1的后面,如果我们按照我们上面模拟实现的 memcpy 的思路去分析,可以发现,arr+1位置的数据,在第一次复制之后,和arr的数据一样了,那么第二次复制时,复制给 arr+2 的数据就也是 arr 的数据了,显然这样不行。

那该怎么办?答案很简单,就是从后往前复制,这样就不会出现上面的问题了。

这样就大功告成了吗?

按照上面的思路,我们来分析一下这个代码:

memmove(arr, arr + 1, 9 * sizeof(int));

如果按照上面的思路从后往前复制,会是什么样的?

第一次复制是将 arr+9 位置的数据复制到 arr+8 ,第二次复制是将 arr+8 的数据复制到 arr+7 ,你看,之前的问题又出现了,arr+7 位置的数据和 arr+9 的数据一样了。

所以应该怎么办?

很简单,分情况:我们发现,当目标空间的起始位置在源空间的起始位置的前面时,我们需要从前往后复制,而目标空间的起始位置在源空间的起始位置的后面时,我们需要从后往前复制,这样就大功告成了。

函数接口:

void* my_memmove(void* dest, const void* source, size_t num)

{ -- -->

char* curdest = (char*)dest;

char* cursou = (char*)source;

if (dest > source)

{

while (num--)

*(curdest + num) = *(cursou + num);

}

else

{

while (num--)

*(curdest++) = *(cursou++);

}

return dest;

}

我们再写 main 函数,对两种结果分别进行测试

int main()

{

int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };

//my_memmove(arr+1, arr , 9 * sizeof(int));//结果1

my_memmove(arr, arr + 1, 9 * sizeof(int));//结果2

for (int i = 0; i < 10; i++)

{

printf("%d ", arr[i]);

}

return 0;

}

结果1:

结果1

结果2:

结果2

可以看到,无论是哪一种情况,<code>my_memmove 函数都实现了预期的效果。

3. memset

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

3. 1 memset 函数的使用

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

使用示例:

#include<stdio.h>

#include<string.h>

int main()

{ -- -->

char str[] = "Hello World.";

memset(str, 'x', strlen(str) - 2);//strlen是求字符串长度的函数

printf("%s\n", str);

return 0;

}

输出结果:

输出结果

看到这里你可能会有疑惑:为什么<code>value是一个int类型的变量却可以接收 char 类型的形参?实际上value在这里接收的是'x'ASCII码值

3. 2 memset 的模拟实现

memcpy 的思路很像,只是没有源位置了,换成了一个 value

#include<stdio.h>

#include<string.h>

void* my_memset(void* ptr, int value, size_t num)

{ -- -->

char* cur = (char*)ptr;

while (num--)

*(cur++) = value;

return ptr;

}

int main()

{

char str[] = "Hello World.";

my_memset(str, 'x', strlen(str) - 2);

printf("%s\n", str);

return 0;

}

输出结果同上。

4. memcmp

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

比较从ptr1和ptr2指针指向的位置开始,向后的num个字节返回值如下:

返回值

<code>memcmp 是将一个字节的内容看做 unsigned char 类型进行比较的,其他的和 strcmp 一致。

图片来源: cplusplus

4. 1 memcmp 函数的使用

使用示例:

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

}

输出结果:

输出结果

4. 2 memcmp 的模拟实现

思路基本和 <code>strcmp 一致,只是多了一步强制类型转换,这里不再赘述。

#include <stdio.h>

#include <string.h>

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)

{ -- -->

unsigned char* cur1 = (unsigned char*)ptr1;

unsigned char* cur2 = (unsigned char*)ptr2;

while (num--)

{

if (*cur1 != *cur2)

return *cur1 - *cur2;

cur1++, cur2++;

}

return 0;

}

int main()

{

char buffer1[] = "DWgaOtP12df0";

char buffer2[] = "DWGAOTP12DF0";

int n;

n = my_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;

}

如果喜欢这篇博客的话不妨顺手点个赞,收藏,评论,关注!

我会持续更新更多优质文章!!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。