在C语言中经常会操作内存中的数据,下面来介绍一下常用的一些内存操作函数。
memcpy
memcpy用于从source的位置开始向后复制num个字节到destination的内存位置,其函数原型如下:
cpp
//destination是目标地址,source是源地址,num是要复制的字节数,返回的是目标地址。因为不知道内存中存储的是什么数据类型,所以都用void*
void * memcpy ( void * destination, const void * source, size_t num );
下面举一个应用的例子:
cpp
#include <stdio.h>
#include <string.h>
struct
{
char name[40];
int age;
} person, person_copy;
int main()
{
char myname[] = "Pierre de Fermat";
/* using memcpy to copy string: */
memcpy(person.name, myname, sizeof(myname));
person.age = 46;
/* using memcpy to copy structure: */
memcpy(&person_copy, &person, sizeof(person));
printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);
return 0;
}
需要注意的是,C标准规定使用memcpy拷贝的目标地址和源地址空间不能出现重叠 ,否则会出错(vs编译器对memcpy进行了优化,使其空间可以重叠):
cpp
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
//这里是想把12345拷贝到34567的位置,预期结果是12123458,按照C语言标准,如果使用memcpy当把12拷贝到34位置后,继续应该把34拷贝到56的位置,而此时34已经变成了12,所以还会把12拷贝到56,然后把5拷贝到7,此时5已经变成了1,所以最后会得到12121218
memmove(arr + 2, arr, 5 * sizeof(int));
for (int i = 0; i < 8; i++)
{
printf("%d ", arr[i]);//这里可以正常打印出12123458,是因为vs编译器进行了优化
}
return 0;
}
下面来模拟实现一下memcpy函数:
cpp
/*
* @brief 内存拷贝函数,将src地址开始的num个字节的数据拷贝到dest
* @param dest:目标地址
* @param src:源地址
* @param num:拷贝的字节数
* @return void*: 返回dest
*/
void *my_memcpy(void *dest, const void *src, size_t num)
{
assert(dest && src);//防止传入空指针
void *temp = dest;//用于返回
while (num--)//循环num次,每次拷贝一个字节
{
*(char *)dest = *(char *)src;//先把两个地址强转成char*,每次操作一个字节
dest = (char *)dest + 1;//操作完一次后地址++
src = (char *)src + 1;
}
return temp;//返回目标地址
}
memmove
memmove就是在memcpy的基础上增加了处理重叠内存的功能,其函数原型如下:
cpp
//destination是目标地址,source是源地址,num是要复制的字节数,返回的是目标地址。因为不知道内存中存储的是什么数据类型,所以都用void*
void * memmove ( void * destination, const void * source, size_t num );
所以当destination小于source时,应该从前向后 拷贝, destination大于source时,应该从后向前拷贝,下面来模拟实现一下memmove:
cpp
/*
* @brief 内存移动,源和目标地址重叠时的memcpy
* @param dest:目标地址
* @param src:源地址
* @param num:要拷贝的字节数
* @return void*:返回目标地址
*/
void *my_memmove(void *dest, void *src, size_t num)
{
assert(dest && src);
void *temp = dest;
if (dest < src) // 前->后
{
while (num--)
{
*(char *)dest = *(char *)src;
dest = (char *)dest + 1;
src = (char *)src + 1;
}
}
else
{
while (num--) // 后->前
{
*((char *)dest + num) = *((char *)src + num);
}
}
return temp;
}
memcmp
memcmp用于比较两个内存中的内容是否相等,类似于strcmp,其函数原型如下:
cpp
//ptr1是第一块内存的起始地址,ptr2是第二块内存起始地址,num是要比较的字节数,如果第一块内存内容大于第二块,返回正数,反之返回负数,相等返回0
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
memset
memset用于设置指定内存中的数据,其函数原型如下:
cpp
//ptr是要设置的内存起始地址,value是要设置的值,num是要设置的字节数,返回ptr
void* memset(void* ptr, int value, size_t num);
下面举一些应用例子:
cpp
int main()
{
char arr[] = "hello world";
int arr1[5] = {1,2,3,4,5};
memset(arr,'x',5*sizeof(char));
printf("%s\n",arr);//会打印xxxxx world,把前5个字节替换成了x
memset(arr1,0,5*sizeof(int));
for(int i = 0; i < 5; i++)
{
printf("%d ",arr1[i]);//会打印00000,将5个int型,也就是20个字节换成了0
}
memset(arr1,1,5*sizeof(int));
//会打印16843009 16843009 16843009 16843009 16843009,是将每个字节都换成了1,一个int有四个字节,所以如果想将一个int数组的每个元素设置成0以外的数,不能用memset
for(int i = 0; i < 5; i++)
{
printf("%d ",arr1[i]);
}
}