自学内容网 自学内容网

C语言内存函数

一、memcpy函数

🍏🍏mem其实就是memory单词的缩写,就是内存的意思。内存函数顾名思义就是操作内存的函数。通过访问地址的方式对内存中的数据进行操作。内存函数可以应用于任何类型的对象

1.memcpy函数的使用

memcpy是针对内存块进行拷贝的,memcpy函数的使用需要包含头文件string.h。函数的声明如下:

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

🍆函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
🍆这个函数在遇到’\0’的时候并不会停下来。
🍆如果source和destination有任何的重叠,那复制的结果可能是未定义的。

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);//将arr1里的前5个整型拷贝到arr2所指向的内存空间
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}

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

2.memcpy函数的模拟实现

在memcpy函数拷贝结束以后,会返回目标空间的起始地址。那现在我们模拟实现一个memcpy函数:

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
int i = 0;
while(num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 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 };
my_memcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}

程序运行结果:
在这里插入图片描述如果source和destination有任何的重叠,那复制的结果可能是未定义的,这句话怎么理解呢?看下面的一个例子:

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

上面是用自定义的函数my_memcpy进行拷贝,将数组arr1中的前五个整型拷贝放到arr1+2(从arr1数组的第三个元素空间处开始存放5个整型)所指向的空间里,这里就是source和destination有重叠的情况。我们想要的拷贝结果其实是:

1 2 1 2 3 4 5 8 9 10

但实际的运行结果为:
在这里插入图片描述仔细想一下,就能知道当把1、2先拷贝放到arr+2和arr+3的地址处,其实原来数组的3和4就被1和2覆盖了,所以之后再来拷贝arr1+2和arr1+3地址处的元素到arr1+4和arr1+5所指向的空间其实也是拷贝了1和 2。后面的拷贝也是一样的原因,所以出现了上面的结果。但使用内存函数memcpy进行上面source和destination有重叠的拷贝,其实是可以打印出我们想要的效果的,因为memcpy函数在当前的VS编译器下的复制功能是比较完善的。那为什么说有重叠种情况下的复制结果是未定义的呢?其实memcpy函数只需要用来拷贝那些source和destination不重叠的情况即可,而有重叠情况的拷贝可以用别的内存函数来完成。

二、memmove函数

1.memmove函数的使用

memmove函数的声明如下:

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

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

#include<stdio.h>
#include<string.h>
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;
}

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

2.memmove函数的模拟实现

注意:memmove函数拷贝完成以后,会返回目标空间的起始地址。我们可以模拟实现一个和memmove函数一样功能的函数,先分析一下要怎么拷贝:其实有重叠的拷贝有三种情况,假设现在有一个数组:

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

🧀由于数组在内存中是连续存放的,并且数组元素随着下标的增长,地址由低到高。

①如果要拷贝的目标空间的地址dest小于源空间的地址src,比如现在目标空间的地址为arr,源空间的地址为arr+2,要将这个数组中的3,4,5,6,7拷贝放到1,2,3,4,5的位置,那么可以从前往后拷贝。什么意思呢?看下面的示意图:
在这里插入图片描述
当目标空间的地址dest小于源空间的地址src时,用从前向后的方式进行拷贝,可以避免源空间里要拷贝的数据被覆盖。

②如果目标空间的地址dest大于源空间的地址src时,还是上面假设的数组,如果现在目标空间的地址为arr+2,源空间的地址为arr,要将这个数组中的1,2,3,4,5拷贝放到3,4,5,6,7的位置,那么可以从后往前拷贝
在这里插入图片描述在dest>src这种情况下,用从后往前的方式进行拷贝,也是为了避免源空间里要拷贝的数据被覆盖。

③上面的两种情况,不管是dest<src还是dest>src,都是有重叠的情况,那第三种情况就是没有重叠的情况,比如:
在这里插入图片描述没有重叠的情况,无论是从前往后拷贝还是从后往前拷贝都是没有问题的,因为不会出现源空间里要拷贝的数据被覆盖的情况。那我们统一 一下,如果是目标空间的地址dest小于源空间的地址src的情况,我们就采用从前向后的方式拷贝;如果是目标空间的地址dest大于源空间的地址src的情况,我们就采用从后往前的方式拷贝;这样所有可能的情况就都考虑到了。代码的实现方式如下:

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
src = (char*)src + 1;
dest = (char*)dest + 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 };
my_memmove(arr, arr+2, 5*sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}

程序的运行结果:(从前向后拷贝)
在这里插入图片描述上面是模拟在有重叠的情况下进行拷贝,最重要的是有会分析实现有重叠拷贝的思路,这种思路特别在后面学习数据结构的时候也可能会用到。

三、memset函数

memset函数是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。函数声明如下:

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

🥑将ptr指向的内存块的前num个字节设置为指定的值value。 (value值作为int类型传递,但函数使用该值的unsigned char转换来填充内存块)

举一个例子,假如现在有一个字符数组arr,里面存放着"hello world",现在我们想把这个数组里的前5个字符设置为字符’x’,就可以使用memset函数:

#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "hello world";
memset(arr, 'x', 5);//以字节为单位来设置的
printf("%s\n", arr);
return 0;
}

程序运行的结果:
在这里插入图片描述当然memset函数也可以设置其他类型的数组,不一定是char类型的数组。但是要切记:memset设置指定的值是以(1个)字节为单位来设置的,而不能以(数组)元素为单位来设置值。上面char类型的数组能以元素为单位来设置值,是因为char类型数组的每个元素就占一个字节。要是换成整型数组就不能一个元素一个元素的来设置值了。

四、memcmp函数

memcmp函数的功能是:将ptr1所指向的内存块的前num字节与ptr2所指向的前num字节进行比较,如果它们都匹配(相同)则返回0,如果不匹配则返回不同于0的值,表示哪个值更大。函数的声明如下:

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

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

在这里插入图片描述memcmp函数在内存进行比较时,其实也是一对字节一对字节的进行比较,如果第一次比较到不匹配的字节,那返回值就是看ptr1和ptr2所指向的那个字节的值谁大?如果ptr1所指向的字节的值比ptr2所指向的大,则返回大于0的值;如果ptr1所指向的字节的值比ptr2所指向的小,则返回<0的值;如果比较完了内存块中的num个字节的值,ptr1和ptr2所指向的字节的值都一样,则返回0。

#include<stdio.h>
#include<string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 1,2,3,4,5,6,7,8,8,8 };
char arr3[] = "abcdefghijk";
char arr4[] = "abcdefgxyz";

int ret1 = memcmp(arr1, arr2, 36);
int ret2 = memcmp(arr1, arr2, 16);
int ret3 = memcmp(arr3, arr4, 8);
printf("%d\n", ret1);
printf("%d\n", ret2);
printf("%d\n", ret3);
return 0;
}

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


原文地址:https://blog.csdn.net/m0_62183318/article/details/142201942

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!