[C语言]内存操作函数详解(memcpy、memmove、memcmp、memset、memchr)
一. memcpy函数
1.1 函数原型及函数实现的功能
函数原型:void* memcpy(void* destination, const void* source, size_t num)
函数功能:将源空间source中前num个字节的内容拷贝到目标空间destination。
几点注意事项:
- 函数的功能是拷贝前num个字节的内容,不是拷贝前num个元素。
- 与字符串拷贝函数strcpy不同,memcpy函数在拷贝的过程中遇到''不会停止拷贝。
- memcpy不止可以用于拷贝字符,还可以用于拷贝整型、浮点型数据等。
1.2 memcpy函数的使用方法演示
代码段1.1实现的功能是将整型数组arr2中的前5个元素拷贝到arr1中去。那么,在使用memcpy函数时,目标空间为arr1,源空间为arr2,总共需要拷贝5*sizeof(int)个字节的内容。程序运行的结果为1 2 3 4 5 0 0 0 0 0。
两点注意事项:
- memcpy函数的返回类型为void*,如果要使用一个参数来接收memcpy函数的返回值,则应进行强制类型转换,如int* pa = (int*)memcpy(arr1, arr2, 5 * sizeof(int))所示的格式。
- C语言标准不要求memcpy函数可以处理重叠的内存空间,虽然在VS2019编译环境下,memcpy函数可以处理重叠空间,但由于C标准不要求其具有处理重叠空间的能力,因此,应当避免使用memcpy处理重叠空间。如:int arr[6]={1,2,3,4,5,6}; memcpy(arr, arr+1,4),会出现空间重叠,不能这样处理。
memmove函数可以用来处理重叠的空间,本文第二章会进行讲解。
代码段1.1:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[10] = { 0 };
int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = (int*)memcpy(arr1, arr2, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(pa + i));
}
return 0;
}
1.3 memcpy函数的模拟实现
模拟实现代码:
void* my_memcpy(char* dest, const char* src, size_t num)
{
char* ret = dest; //返回值
while (num--)
{
*(char*)dest = *(char*)src;
//这里需要dest指针和src指针均向后移动一个字节
//但是由于void*不能进行加法运算
//因此,要先将空类型转换为字符型指针,然后再+1
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
二. memmove函数
2.1 函数原型及函数实现的功能
函数原型:void *memmove( void *dest, const void *src, size_t num);
函数功能:将源空间src的前num个字节的内容移动到目标空间dest中去。与memcpy函数不同,memmove函数可以处理重叠的空间。
2.2 memmove函数的使用方法演示
- 使用memmove函数处理不重叠的空间
代码段2.1使用memmove函数将整型数组arr2的前5个元素移动到arr1中,效果与使用memcpy一致。
代码段2.1:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9 };
int* pa = (int*)memmove(arr1, arr2, 5 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
- 使用memmove函数处理重叠的内存空间
代码段2.2使用memmove函数将整形数组arr的前5个替换为第3-7个元素的内容,程序运行的结果为:2 3 4 5 6 5 6 7 8 9。
代码段2.2:
#include<stdio.h>
#include<string.h>
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* pa = (int*)memmove(arr, arr + 2, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(pa + i));
}
return 0;
}
2.3 memmove函数的模拟实现
由于memmove函数可以处理重叠的空间,因此,在模拟实现时要考虑两块空间的相互干涉问题。对此,分为三种情况进行讨论(假设要移动num个字节的内容):
- 源空间的起始位置在目标空间的起始位置前方不超过num个字节内存的位置。此时,如果从前往后进行复制,则会发生干涉,应当从后往前进行内存内容的复制,图解如图2.1所示。
- 源空间的起始位置在目标空间后方不超过num个字节内存的位置。此时,如果从后往前复制会发生干涉,应当从前往后复制,图解如图2.2所示。
memmove函数模拟实现的代码:
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
if (src <= dest)
{
//src的小于dest
//从后往前复制
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
else
{
//src的大于dest
//从前往后复制
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}
三. memcmp函数
3.1 函数原型及实现的功能
函数原型:int memcmp(const void* ptr1, conat void* ptr2, size_t num);
函数功能:比较两块内存空间ptr1和ptr2前num个字节的空间的存储内容的大小。比较方式和返回值与strncmp基本相同。两者的不同之处是strncmp遇到会停止比较,而memcmp不会。
3.2 memcmp函数的使用方法演示
代码段3.1定义了两个字符型数组:arr1[] = "abcdef" 和 arr2[] = "abceef",分别通过strncmp和memcmp函数比较arr1和arr2前5个字符的大小,将返回值分别存入ret1和ret2中,程序运行结果显示ret1=0、ret2<0。因此,通过strncmp比较字符串前5个字符的结果为arr1=arr2,通过memcmp比较arr1和arr2前5个字节内存的内容的结果为arr1<arr2。可见,memcmp遇到并没有停止比较。
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abceef";
int ret1 = strcmp(arr1, arr2, 5);
printf("ret1 = %dn", ret1);
int ret2 = memcmp(arr1, arr2, 5);
printf("ret2 = %dn", ret2);
return 0;
}
3.3 memcmp函数的模拟实现
模拟实现代码:
int my_memcmp(const void* str1, const void* str2, size_t num)
{
while(num--)
{
if ((*(char*)str1 == *(char*)str2))
{
str1 = (char*)str1 + 1;
str2 = (char*)str2 + 1;
}
else
{
return *(char*)str1 - *(char*)str2;
}
}
return 0;
}
四. memset函数
4.1 函数原型及函数的功能
函数原型:void *memset( void *dest, int c, size_t count );
函数功能:memset为内存设置函数,将目标空间dest前count个字节的内存空间的内容设置为c,因为字符型数据属于整型家族,因此在函数调用时,可以将字符型数据作为c传递给memset函数。函数的返回值为指向dest空间的起始位置的指针。
4.2 memset函数的使用方法演示
代码段4.1实现的功能是将字符数组arr1的前5个字符改为'x',程序运行解结果为:arr = xxxxx###
代码段4.1:
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "########";
char* ret = memset(arr, 'x', 5);
printf("arr = %sn", ret); //arr = xxxxx###
return 0;
}
4.3 memset函数的模拟实现
模拟实现代码:
void* my_memset(void* str, int c, size_t num)
{
assert(str); //指针有效性检验
void* ret = str;
while (num--)
{
*(char*)str = c;
str = (char*)str + 1;
}
return ret;
}
五. memchr函数
5.1 函数原型及函数的功能
函数原型:void *memchr( const void *buf, int c, size_t count );
函数功能:从buf指向的空间为起始位置,在后面count个字节的内存空间中寻找第一次出现c的位置,并返回指向第一次出现c的位置的指针,如果找了count个字节的内存还没有发现c,函数就返回空指针NULL。
5.2 memchr使用方法的演示
在代码段5.1中,以字符数组arr[]="abcdabcf"的首元素位置为起始点,在7个字节的范围内,分别查找字符b和字符f第一次出现的位置,程序运行的结果为:ret1 = bcdabcf、ret2 = (null)。
代码段5.1:
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdabcf";
char* ret1 = (char*)memchr(arr, 'b', 7);
printf("ret1 = %sn", ret1);
char* ret2 = (char*)memchr(arr, 'f', 7);
printf("ret2 = %sn", ret2);
return 0;
}
5.3 memchr的模拟实现
模拟实现代码:
void* my_memchr(const void* str, int c, size_t count)
{
assert(str);
while (count--)
{
if (c == *(char*)str)
{
return str;
}
str = (char*)str + 1;
}
return NULL;
}
全文结束,感谢大家的阅读,敬请批评指正。