前言
本篇博客主要介绍常用的4个内存操作函数:
1.不重叠内存拷贝函数 – memcpy()
2.重叠内存拷贝函数 – memmove()
3.内存比较函数 – memcmp()
4.内存填充函数 – memset()
使用这4个函数均需要包含头文件string.h
文章目录
- 前言
- 1. 不重叠内存拷贝函数 -- `memcpy()`
- 1.1 函数功能介绍
- 1.2 参数及返回值介绍
- 1.3 使用`memcpy`拷贝各类型数据
- 1.3.1 代码
- 1.3.2 输出结果
- 1.3.3 解释
- 1.4 使用细节
- 1.5 `memcpy`函数的模拟实现
- 2. 重叠内存拷贝函数 -- `memmove()`
- 2.1 函数功能介绍
- 2.2 `memmove()`的实现原理
- 2.2.1 第一种重叠形式
- 2.2.2 第二种重叠形式
- 2.2.3 总结
- 2.3 模拟实现`memmove()`
- 3.内存比较函数 -- `memcmp()`
- 3.1 函数功能介绍
- 3.2 代码举例
- 3.2.1 比较整型数据
- 3.2.2 比较字符串(对比`strncmp`)
- 4. 内存填充函数 -- `memset()`
- 4.1 初始化`malloc`开辟的数组
- 4.2 初始化结构体变量
- 4.3 初始化结构体数组
1. 不重叠内存拷贝函数 – memcpy()
1.1 函数功能介绍
函数memcpy
将source
指向的内存空间的前num
个字节的数据复制到destination
指向的空间中。
1.2 参数及返回值介绍
函数原型:
void* memcpy ( void * destination, const void * source, size_t num );
-
参数1:
destination
– 指向用于存储复制内容的目标空间,类型强制转换为void*
指针。 -
参数2:
source
– 指向要复制的数据源,类型强制转换为void*
指针,const
关键字保证数据源不会被修改。 -
参数3:
num
– 要被复制的字节数。 -
返回值:目标空间地址
destination
,类型为void*
1.3 使用memcpy
拷贝各类型数据
1.3.1 代码
(来自网站cplusplus)
#include <stdio.h>
#include <string.h>struct {char name[40];int age;
} person, person_copy;int main()
{char myname[] = "张三";/* 1. 使用 memcpy 拷贝 字符串 */memcpy(person.name, myname, strlen(myname) + 1);person.age = 46;/* 2. 使用 memcpy 拷贝 结构体数据: */memcpy(&person_copy, &person, sizeof(person));printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);/* 3. 使用 memcpy 拷贝 数组数据 */int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int arr_copy[10] = { 0 };memcpy(arr_copy, arr, sizeof(arr));printf("arr_copy: ");for (int i = 0; i < 10; i++)printf("%d ", arr_copy[i]);return 0;
}
1.3.2 输出结果
person_copy: 张三, 46
arr_copy: 1 2 3 4 5 6 7 8 9 10
1.3.3 解释
memcpy(person.name, myname, strlen(myname) + 1);
- 第一个参数
person.name
是指向待放数据数组首元素的指针变量。 - 第二个参数
myname
是指向待拷贝的源头数组首元素的指针变量。 - 第三个参数为表达式
strlen(myname) + 1
,strlen(myname)
表示字符串长度(单位:字节),+1
表示将'\0'
拷贝过去
- 第一个参数
memcpy(&person_copy, &person, sizeof(person));
- 第一个参数
&person_copy
是待放拷贝数据的结构体空间首地址。 - 第二个参数
&person
是待拷贝的源头结构体空间首地址。 - 第三个参数
sizeof(person)
,表示将结构体person的全部内容拷贝到结构体person_copy
(单位:字节)。
- 第一个参数
memcpy(arr_copy, arr, sizeof(arr));
- 第一个参数
arr_copy
是待放拷贝数据的数组首元素地址。 - 第二个参数
arr
是待拷贝的源头数据数组首元素地址。 - 第三个参数
sizeof(arr)
表示拷贝内容大小为40个字节
- 第一个参数
1.4 使用细节
- 这个函数在遇到 ‘\0’ 的时候并不会停下来。
2. 前面讲到过,memcpy
是使用在不重叠的内存块之间的拷贝,但是,如果source
和destination
有任何的重叠,那么拷贝的结果又当如何——答案是不知道!
C语言官方文档中未对该状况做出明确定义,所以具体情况得看编译器提供的库函数是如何实现的。
假设使用
memcpy
进行重叠内存拷贝的预期结果:
实际运行的结果:
由于内存重叠,在拷贝过程中内存中的数据会受到改变,因此实际结果并非我们预期说想的结果。
- VS里面
memcpy
超额完成了功能,它能够进行重叠内存的拷贝。也就是使其拥有memmove
的功能至于memmove
又有什么功能,别急,下一个介绍的函数就是它。
1.5 memcpy
函数的模拟实现
#include <assert.h>
void* myMemcpy(void* dest, const void* src, size_t num)
{assert(dest && src);for (size_t i = 0; i < num; i++)((char*)dest)[i] = ((char*)src)[i];return dest;
}
2. 重叠内存拷贝函数 – memmove()
2.1 函数功能介绍
void * memmove ( void * destination, const void * source, size_t num );
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
2.2 memmove()
的实现原理
memmove
很好地解决了memcpy
在拷贝过程中源内存空间内容因被覆盖而被改变的问题
上面已经把为何会导致出现问题的原因讲的很详细了,所以这里的重点就放在memmove
如何解决这个问题上来——在模拟实现memmove()
的过程中进行解答
2.2.1 第一种重叠形式
从上面的拷贝原理图中看到,从src
指向空间的数据被从低地址向高地址拷贝(从前向后拷贝)是行不通的。但是,拷贝的方式又不是只有这一种,为什么不试试从后向前拷贝呢?
图解如下:
拷贝过程中,即使内存中的数据本身被覆盖了,但是源数据本身该被拷贝的数据却并没有没改变。
2.2.2 第二种重叠形式
解决了上面第一种内存重叠情况的拷贝后,当出现遇到第二种内存重叠情况时,仔细分析一下不难发现这种情况下,更适合从前向后拷贝。
图解如下:
2.2.3 总结
以src为界,
当dest < src
时, 源空间数据从前向后拷贝。
当dest >= src
时,源空间数据从后向前靠拷贝。
2.3 模拟实现memmove()
include <assert.h>
void* myMemmove(void* dest, const void* src, size_t num)
{assert(dest && src);if (dest < src){for (size_t i = 0; i < num; i++)((char*)dest)[i] = ((char*)src)[i];}else{while(num--)((char*)dest)[num] = ((char*)src)[num];}return dest;
}
3.内存比较函数 – memcmp()
函数原型
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
3.1 函数功能介绍
字节为单位,比较内存空间ptr1
和ptr2
的前num
个字节的数据大小。
ptr1
指向内存块1,类型为void*
,可接受任何类型数据ptr2
指向内存块2,类型为void*
,可接受任何类型数据num
表示待比较字节数-
返回值规则如下(图片来自cplusplus):
3.2 代码举例
3.2.1 比较整型数据
第一次比较1个字节,arr2指向的第1个字节 > arr1指向的第1个字节,返回负数。
第二次预计比较21个字节,但是在第一个字节比较完毕时已返回比较结果
3.2.2 比较字符串(对比strncmp
)
从上面的代码例子可以看出,
memcmp
与strncmp
二者之间的功能在大致上是相似的,但是,当字符> 串长度小于num
时,strncmp
遇到'\0'
会结束,而memcmp
不会,它继续比较,直到比较完num
个字节
4. 内存填充函数 – memset()
void * memset ( void * ptr, int value, size_t num );
一言蔽之,函数将ptr
指向的内存空间的前num
个字节设定为默认值value
,最后返回ptr
这个函数比较适合用来初始化一些书写不太方便的变量或者数组。
4.1 初始化malloc
开辟的数组
两种初始化方式,使用memset
比较方便
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main()
{int* arr1 = (int*)malloc(sizeof(int) * 10);int* arr2 = (int*)malloc(sizeof(int) * 10);//arr1初始化为全0memset(arr1, 0, sizeof(int) * 10);//arr2初始化为全0for (int i = 0; i < 10; i++)arr2[i] = 0;return 0;
}
4.2 初始化结构体变量
#include<stdio.h>
#include<string.h>struct student{int ID;char name[20];
};int main(){struct student stu;memset(&stu, 0, sizeof(stu));printf("%d\t%s", stu.ID, stu.name);return 0;
}
4.3 初始化结构体数组
#include<stdio.h>
#include<string.h>struct student{int ID;char name[20];
};int main(){struct student stus[10];memset(stus, 0, sizeof(stus));printf("%d\n", stus[0].ID);printf("%s", stus[0].name);return 0;
}