一、memmove函数说明
还是老规矩给大家伙贴上cplusplus的网址。
函数总结:
-
重叠安全:
- 即使
destination
和source
指向的内存区域有重叠,memmove
也能安全地复制数据,因为它使用了一个中间缓冲区。
- 即使
-
数据类型无关性:
- 函数在复制数据时不关心
source
和destination
指针所指向对象的底层类型。
- 函数在复制数据时不关心
-
二进制复制:
- 数据以二进制形式复制,不检查
source
中是否有终止空字符(null terminator)。
- 数据以二进制形式复制,不检查
-
精确字节数:
- 函数总是精确复制
num
指定的字节数。
- 函数总是精确复制
-
内存大小要求:
- 为了避免溢出,
destination
和source
指向的数组的大小至少应该是num
字节。
- 为了避免溢出,
使用 memmove
时,开发者需要确保提供的内存区域大小合适,并且正确处理任何可能的内存重叠问题,以避免数据损坏或程序崩溃。
二、memmove函数使用
#include <stdio.h>
#include <string.h> // 包含memmove函数的头文件
int main() {
char src[] = "Hello, World!";
char dest[20]; // 确保目标数组足够大以容纳源数组的内容
// 将src数组的内容复制到dest数组中
memmove(dest, src+6, strlen(src) + 1); // 包括空字符'\0'
printf("Source: %s\n", src);
printf("Destination after memmove: %s\n", dest);
return 0;
}
这段代码首先包含了 string.h
头文件,这是使用 memmove
函数所必需的。然后定义了两个字符数组 src
和 dest
。src
包含了要复制的字符串,而 dest
是目标数组,用于存储复制的数据。我们使用 memmove
函数将 src
的内容复制到 dest
,包括字符串的终止空字符 \0
。复制完成后,使用 printf
函数打印出源字符串和目标字符串,以展示 memmove
函数的效果。
请注意,这个示例假设 dest
数组的大小足够大,可以容纳 src
数组的全部内容。在实际使用中,你需要根据实际情况调整数组的大小,并确保不会发生缓冲区溢出。
三、memmove模拟实现
#include <stdio.h>
#include <assert.h>
#include <string.h> // 包含size_t类型的定义
void* my_memmove(void* dest, const void* src, size_t count) {
assert(dest && src);
void* ret = dest;
char* d = (char*)dest;
const char* s = (const char*)src;
if (s < d && s + count > d) {
// 逆序复制,因为src在dest之前且有重叠
s += count - 1;
d += count - 1;
while (count--) {
*d-- = *s--;
}
} else {
// 顺顺序复制,因为src在dest之后或者没有重叠
while (count--) {
*d++ = *s++;
}
}
return ret;
}
// 数组的打印
void print(int* arr, size_t sz) {
for (size_t i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
}
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 复制从arr[1]开始的5个元素到arr[3]的位置
my_memmove(arr + 3, arr + 1, 5);
size_t sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz); // 数组的打印
printf("\n");
return 0;
}
代码详解:
-
自定义
my_memmove
函数:- 函数原型:
void* my_memmove(void* dest, const void* src, size_t count)
,接受三个参数,分别代表目标内存地址、源内存地址和要复制的字节数。 assert(dest && src)
:确保传入的目标和源地址不为空。void* ret = dest
:保存目标地址,以便最后返回。- 将目标和源地址转换为
char*
类型,以便按字节操作。
- 函数原型:
-
内存复制逻辑:
- 首先检查
src
和dest
的相对位置。如果src
在dest
之前并且有重叠,使用逆序复制以避免数据覆盖。 - 如果
src
在dest
之后或者没有重叠,则使用顺顺序复制。
- 首先检查
-
逆序复制:
- 当
src
在dest
之前且有重叠时,将源指针s
和目标指针d
都移动到复制区域的末尾,然后逐字节复制,直到count
减到 0。
- 当
-
顺顺序复制:
- 当
src
在dest
之后或者没有重叠时,从src
的开始位置逐字节复制到dest
的开始位置,直到count
减到 0。
- 当
-
返回值:
- 函数返回目标地址
ret
。
- 函数返回目标地址
-
打印函数:
void print(int* arr, size_t sz)
:定义了一个打印数组的函数,接受一个整型指针和数组的大小。
-
main
函数:- 定义了一个整型数组
arr
并初始化。 - 调用
my_memmove
函数,将arr
中从索引 1 开始的 5 个元素复制到索引 3 的位置。注意这里count
应该是元素个数而不是字节数,但因为sizeof(int)
通常等于 4 字节,所以这里使用 5 作为count
值是安全的,前提是数组元素大小不超过 4 字节。 - 计算数组大小
sz
并使用print
函数打印数组内容。
- 定义了一个整型数组
本期内容到此就结束啦,自己动手实现一下吧。只有自己动手写出来了知识才是自己的哟!