目录
1.前言
上一次学习了C语言中字符串函数有关的内容,这一次来看看内存函数。
而与上次所学不同的是这次的函数操作都是对内存。
2. memcpy使用和模拟实现
2.1 memcpy的使用
我们同样打开链接: https://legacy.cplusplus.com/看看 memcpy
函数的使用。
函数memcpy
从source
的位置开始向后复制num
个字节的数据到destination
指向的内存位置。
来看一个示例:
#include <string.h>
#include <stdio.h>
int main()
{
char arr3[] = "xxxxxxxxxx";
char arr4[] = "yyyyyyyy";
memcpy(arr3, arr4, 3);
printf("%s ", arr3);
return 0;
}
显示结果
结果好像与strcpy
使用的一样,但真的是一样的吗?
其实并不是,memcpy
函数拷贝的是一个字节,在上面的例子中没有很好体现,我们换一个例子看看:
#include <string.h>
#include <stdio.h>
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8 };
memcpy(arr1, arr2, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
我们调试看看,发现要拷贝20个字节,在整型中那就是传入5个整型的值,结果也是这样的。
需要注意的是:这个函数在遇到’\0’ 的时候并不会停下来。
如果source
和destination
有任何的重叠,复制的结果都是未定义的。
2.2 memcpy的模拟实现
既然知道了memcpy
函数怎么使用的,那我们自己编成来实现一下。
既然需要知道传入的字节数,我们就得定义一个变量来接受。
而memcpy
函数是以字节为单位使用,那我们传的类型可能不同,所以要强制类型转换为char类型,这样更方面处理。
实现内存的拷贝,那肯定要把要拷贝的内存地址和拷贝到的内存地址都传入,而我们也并不知道使用者要传入什么类型的,所以使用一个void*
,来接收。
下面我们来看看代码:
#include <string.h>
#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);//因为传入的地址不能为空,所以先断言一下。
while (num--)
{
//拷贝一个字节
*(char*)dest = *(char*)src;//将它们都强制类型转换为char类型
dest = (char*)dest + 1;//拷贝一个字节完成后,继续向后拷贝
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8 };
my_memcpy(arr1, arr2, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
结果与使用memcpy
函数的结果是一致的
那如果要实现自己拷贝自己呢?
此时就会出现内存重叠的情况,而对于这种情况,也有所对应的内存函数,下面我们就来看看memmove
。
3. memmove使用和模拟实现
3.1 memmove的使用
memmove
函数用来实现自身的拷贝。
我们将1,2,3,4,5传给3,4,5,6,7的位置,那么最终实现的应该是1,2,1,2,3,4,5,8,9,10。
那是不是就是这个结果呢?
代码实现一下:
#include <string.h>
#include <stdio.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]);
}
return 0;
}
结果与我们分析的是一样的。
3.2 memmove的模拟实现
我们来模拟实现一下 memmove函数的功能。
那我们就要知道它是怎么实现的。
这个函数在拷贝的时候究竟是从前往后呢,还是从后往前?
这就的视情况而定了。
那就需要判断dest
与 src
的位置关系了。
如果dest < src
就从前往后;如果dest在src + num
之间那就要从后往前;如果dest
在src + num
之后,那两种都可以。
#include <string.h>
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest);
assert(src);
//分情况讨论
if (dest < src)
{
//前->后
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//后->前
while (num--)
{
//在这里num=18
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
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]);
}
return 0;
}
结果与我们用memmove
函数的结果是相同的。
注意:
- 和
memcpy
的差别就是memmove
函数处理的源内存块和目标内存块是可以重叠的。 - 如果源空间和目标空间出现重叠,就得使⽤
memmove
函数处理。
4. memset函数的使用
memset
是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。
我们来看个例子:
将hello world
中的hello
改为xxxxx world
,我们需要将arr首元素地址传入,而后将要修改为x也传入,最后传入要设置是字节数量。
我们来看看代码及结果:
#include <string.h>
#include <stdio.h>
int main()
{
char arr[] = "hello world";
memset(arr, 'x', 5);
printf("%s\n", arr);
return 0;
}
结果显然是所分析的那样。
注意:memset
是以字节为单位设置内存值的。
5. memcmp函数的使用
比较从ptr1和ptr2指针指向的位置开始,向后的num个字节。
返回值如下:
与strcmp
不同的是 memcmp
函数的使用是以字节为单位的。
下面来看一个示例:
#include <string.h>
#include <stdio.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abgfqwertyuiop";
int ret = memcmp(arr1, arr2, 2);
printf("%d\n", ret);
return 0;
}
比较的是前两个字节,而他们两个前两个字节都是相同的,所以返回值就为0。
那当我们不使用char类型来比较时,会是什么情况呢?
#include <string.h>
#include <stdio.h>
int main()
{
int arr1[] = { 1,0x01203,3,4,5 };
int arr2[] = { 1,3,5,7,9 };
int ret = memcmp(arr1, arr2, 5);
printf("%d\n", ret);
return 0;
}
这里结果为0,那是为什么呢?
我们调试,观察内存,我们传入比价的是前5个字节,而在内存发现它们是相同的,结果也当然返回的是0。
有问题请指出,让我们一起共同进步吧!