一、memcpy
(1)函数原型
它的作用是从source位置开始向后复制num个字节到destination指向的内存位置。它与strcpy函数不同的是,strcpy只针对字符串操作,而 memcpy 是针对内存的复制,所以能对所有类型的num字节数据进行操作。
void * memcpy ( void * destination, const void * source, size_t num );
(2)注意事项
- 因为不是针对字符串的,所以遇到'\0'不会停下来。
- 如果source内存块与destination内存块有重叠的部分,那么复制的结果是意料之外的。
例如对于以下代码:
#include<stdio.h>
#include<string.h>
int main() {
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int sz = sizeof(arr) / sizeof(arr[0]);
// 我想把arr的内容改为: 1, 2, 3, 1, 2, 3, 4, 5, 6
memcpy(arr + 3, arr, 6 * sizeof(int));
for (int i = 0; i < sz; i++)
printf("%d ", arr[i]);
}
但实际上是这样的:
最终复制结果是:1, 2, 3, 1, 2, 3, 1, 2, 3
再来看看VS运行的结果:
虽然VS中memcpy能正确复制重叠的src到dest,但这只是VS的编译器做到了,C标准中memcpy复制的结果会是1, 2, 3, 1, 2, 3, 1, 2, 3。
对于重叠的内存复制,由memmove完成。
(3)模拟实现函数
二、memmove
(1)函数原型
这个函数就是memcpy函数的升级版,它能处理源内存块与目标内存块有重叠的情况。
void * memmove ( void * destination, const void * source, size_t num );
(2)模拟实现函数
数组下标的增大,所对应的内存地址也增大。
① 当 src < dest < src + num 时:
如果从前往后依次将src的内容复制到dest,那么src的重叠部分内容,会因为前三个元素复制到dest,而被覆盖掉。为了避免这个问题,可以采用从后往前依次复制(从src的结尾开始倒着复制):
② 当dest < src 时:
如果依旧用从后往前复制,会发现 4 和 5 会被先复制过来的 8和7覆盖掉,造成同样的问题。所以这时应该从前往后复制。
当 dest > src + num 时:
因为没有重叠的地方,所以从前往后和从后往前复制都是可以的。但是在写代码的时候,为了跟 src < dest < src + num 的情况统一为 dest > src,故使用从后往前的顺序复制。
代码及运行结果:
三、memset
(1)函数原型
它的作用是将 ptr 指向的大小为num字节的内存块,按字节为单位设置为value。
void * memset ( void * ptr, int value, size_t num );
(2)函数的使用
如果将value改为1:
会发现每个元素的值并不为1,而是一个很大的数,这是因为memset是按字节设置值的,所以每一个字节都为设置为01。因此,memset通常是用来将内存块的内容设置为0的。
但是如果是设置字符数组的内容,就不会出现上面的问题,因为字符的内存大小就是1个字节:
四、memcpy
(1)函数原型
它的作用的是按字节比较 ptr1 和 ptr2 指向的内存块内容,一共比较num个字节。
ptr1指向的值比ptr2指向的值小,则返回小于0的数;ptr1指向的值比ptr2指向的值大,则返回大于0的数;ptr1指向的值等于ptr2指向的值,则返回等于0的数。
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
(2)函数的使用
在内存中,数字的存放是低位数放低位地址的内存单元,高位数放高位地址的内存单元。因此,我们平时书写1的十六进制是:00 00 00 01,但在内存中存放是倒着的:
故memcpy比较两个内存块,是先比较低位数,再比较高位数的。
虽然在这个例子中,参数num是20,但实际上在比较 arr2的第4个元素3时就已经比较出大小了,所以只比较了16个字节就得到了结果。