memmove
和 memcpy
都是用来复制内存中的数据,但它们之间有一些关键的区别:
1. 处理重叠内存区域的能力
-
memcpy
:- 不支持重叠内存区域的复制。
- 如果源和目标内存区域重叠,
memcpy
的行为是未定义的。 - 通常情况下,
memcpy
会直接从源地址复制到目标地址,而不考虑它们是否重叠。
-
memmove
:- 支持重叠内存区域的复制。
- 当源和目标内存区域重叠时,
memmove
会正确地处理这种情况,确保数据正确复制。 memmove
会根据源和目标的相对位置决定是从后向前还是从前向后复制数据,以避免数据被覆盖。
2. 参数
-
memcpy
:- 函数原型:
void *memcpy(void *dest, const void *src, size_t n);
- 参数说明:
dest
: 目标内存块的起始地址。src
: 源内存块的起始地址。n
: 要复制的字节数。
- 函数原型:
-
memmove
:- 函数原型:
void *memmove(void *dest, const void *src, size_t n);
- 参数说明:
dest
: 目标内存块的起始地址。src
: 源内存块的起始地址。n
: 要复制的字节数。
- 函数原型:
3. 空终止符处理
-
memcpy
:- 不会自动添加或处理空终止符。
- 如果复制的内存块包含字符串,你需要确保复制的字节数包含空终止符,否则复制的字符串将不完整。
-
memmove
:- 同样不会自动处理空终止符。
- 如果复制的内存块包含字符串,你也需要确保复制的字节数包含空终止符。
示例
假设有一个字符串 "hello,8world!"
,并希望将 'w'
到 'd'
的字符移动到逗号后面的位置。
使用 memcpy
1#include <stdio.h>
2#include <string.h>
3
4int main()
5{
6 char str[] = {"hello,8world!"};
7
8 // 复制从 'w' 开始的子字符串到逗号后面
9 memcpy(str + 6, str + 7, 6); // 复制 'w' 到 'd'
10
11 // 添加空终止符
12 str[12] = '\0'; // 在 '!' 之前添加空终止符
13
14 printf("%s\n", str);
15 return 0;
16}
分析
- 原始状态:
h e l l o , 8 w o r l d ! \0
- 修改后:
h e l l o , w o r l d ! \0
memcpy
的调用 memcpy(str + 6, str + 7, 6);
将 'w'
到 'd'
的字符(共 6 个字符)复制到逗号后面的位置。这意味着 'w'
覆盖 '8'
,'o'
覆盖 'w'
,依此类推,直到 'd'
覆盖 'l'
。
接下来,str[12] = '\0';
在 '!'
之前添加了一个空终止符 \0
,确保字符串在 '!'
处结束。
最终状态:
1h e l l o , w o r l d ! \0
20 1 2 3 4 5 6 7 8 9 10 11 12
输出
因为 memcpy
复制了 'w'
到 'd'
的字符,并且在 '!'
之前添加了空终止符 \0
,所以字符串现在变成了 "hello,world"
,并且只有一个感叹号。printf
函数遇到 '!'
后面的空终止符 \0
时会停止输出,因此输出将是 "hello,world"
。
这种方法使用 memcpy
来完成字符的复制,并手动添加空终止符来确保字符串正确结束。
使用 memmove
1#include <stdio.h>
2#include <string.h>
3
4int main()
5{
6 char str[] = {"hello,8world!"};
7
8 // 复制从 'w' 开始的子字符串到逗号后面
9 memmove(str + 6, str + 7, 6); // 复制 'w' 到 'd'
10
11 // 添加空终止符
12 str[12] = '\0'; // 在 '!' 之前添加空终止符
13
14 printf("%s\n", str);
15 return 0;
16}
分析
- 原始状态:
h e l l o , 8 w o r l d ! \0
- 修改后:
h e l l o , w o r l d ! \0
memmove
的调用 memmove(str + 6, str + 7, 6);
将 'w'
到 'd'
的字符(共 6 个字符)移动到逗号后面的位置。这意味着 'w'
覆盖 '8'
,'o'
覆盖 'w'
,依此类推,直到 'd'
覆盖 'l'
。
接下来,str[12] = '\0';
在 '!'
之前添加了一个空终止符 \0
,确保字符串在 '!'
处结束。
最终状态:
1h e l l o , w o r l d ! \0
20 1 2 3 4 5 6 7 8 9 10 11 12
输出
因为 memmove
复制了 'w'
到 'd'
的字符,并且在 '!'
之前添加了空终止符 \0
,所以字符串现在变成了 "hello,world"
,并且只有一个感叹号。printf
函数遇到 '!'
后面的空终止符 \0
时会停止输出,因此输出将是 "hello,world"
。
这种方法使用 memmove
来完成字符的移动,并手动添加空终止符来确保字符串正确结束。
总结
-
选择:
- 如果源和目标内存区域不重叠,可以使用
memcpy
。 - 如果源和目标内存区域可能重叠,应使用
memmove
。
- 如果源和目标内存区域不重叠,可以使用
-
注意事项:
- 不论使用哪个函数,都需要确保复制的字节数正确,特别是当涉及到字符串时。
- 如果复制的内存块包含字符串,需要确保复制的字节数包括空终止符
\0
。
思考
如果第三个参数为7或者8会是什么情况呢?
如果为7 会把最后一位‘\0’直接赋值到‘!’,不用str[12] = '\0';
- 原始状态:
h e l l o , 8 w o r l d ! \0
- 修改后:
h e l l o , w o r l d ! \0
如果为8呢?
- 原始状态:
h e l l o , 8 w o r l d ! \0
- 修改后:
h e l l o , w o r l d ! \0 \0
如果>=9呢,会出现什么情况呢 ?
这个时候会越界?还是其他?请大家思考一下。