当src与dst内存重叠时,指针循环复制为何与memmove效果不同?
内存重叠时手动循环复制与memmove执行效果差异的原因
先看你给出的两段代码:
手动循环复制代码:
for (int i = 0; i < size; i++) { dst[i] = src[i]; }
memmove调用代码:
memmove(dst, src, size);
两者执行效果的差异,核心在于内存重叠场景下的复制顺序逻辑不同:
1. 手动循环的复制逻辑
手动循环是固定从内存低地址到高地址依次复制,也就是按索引从0到size-1的顺序,把src[i]的值赋给dst[i]。这种顺序在内存区域重叠时,会出现“还没读取的src数据被提前覆盖”的问题:
举个具体例子:假设我们有数组int arr[] = {1, 2, 3, 4};,src = arr(指向数组起始地址),dst = arr + 1(指向数组第2个元素的地址),size = 3。手动循环的执行过程:
- i=0:
dst[0] = src[0]→ arr[1]被改成1,数组变为{1, 1, 3, 4} - i=1:
dst[1] = src[1]→ 此时src[1]已经是被修改后的1,arr[2]被改成1,数组变为{1, 1, 1, 4} - i=2:
dst[2] = src[2]→ src[2]已经是被修改后的1,arr[3]被改成1,最终数组变成{1, 1, 1, 1}
这显然不是预期的结果——我们原本想把arr的前3个元素1,2,3复制到从第2个元素开始的位置,得到{1,1,2,3},但手动循环因为顺序问题破坏了原始数据。
2. memmove的处理逻辑
memmove内部会先判断src和dst的内存地址关系,自动调整复制顺序:
- 如果
dst的起始地址在src前面,或者两者无重叠,就用和手动循环一致的低地址到高地址顺序复制; - 如果
dst的起始地址在src后面且存在重叠(即src < dst < src + size),就会改用高地址到低地址的顺序复制,也就是从索引size-1开始倒着复制,避免覆盖还未读取的src原始数据。
还是用刚才的例子,memmove的执行过程:
- i=2:
dst[2] = src[2]→ arr[3]被改成3,数组变为{1, 2, 3, 3} - i=1:
dst[1] = src[1]→ arr[2]被改成2,数组变为{1, 2, 2, 3} - i=0:
dst[0] = src[0]→ arr[1]被改成1,最终数组变成{1, 1, 2, 3},完全符合预期。
简单来说,手动循环是“一根筋”从前往后复制,不管内存重叠情况;而memmove会根据内存布局智能调整复制方向,确保重叠区域的原始数据不会被提前覆盖。
内容的提问来源于stack exchange,提问作者Yanir
相关产品推荐
相关产品推荐

