MASM32中如何高效将多个数组合并为单个目标数组
问题根因
你的两种实现运行出错核心是犯了3类低级错误:
- 第一种
crt_memcpy写法的问题:- 根本没给
arrayOut分配足够内存:你在.data?段只定义了一个dd类型变量,仅占4字节,完全存不下几千字节的合并数据,写入时会直接越界覆盖其他段的有效数据 - 第一次调用
crt_memcpy的目标地址写错:传入的是不存在的array符号,不是输出数组的有效地址 - 指针逻辑完全错误:
arrayOut本身是指针变量,你在没给它赋值有效内存地址的前提下,直接取它的值加偏移当写入目标,属于典型的野指针写入
- 根本没给
- 第二种手写循环的问题:
- 同样没有给
arrayOut分配足够内存,直接向标号地址写入必然越界 - 循环计数错误:源数组是
db类型的单字节元素,你把计数设为sizeof array1 *2,平白多拷一倍长度,必然触发越界 - 拷贝逻辑错误:读入1个字节后做
movsx符号扩展成4字节写入目标,相当于把1字节源数据扩成4字节存储,目标数组的内存布局完全错乱,和源数组的字节序列根本不匹配 - 循环逻辑冗余:
loop指令本身会自动递减ecx并判断是否为0,你额外加的cmp+jbe判断完全多余,还容易写错跳转条件导致循环次数异常
- 同样没有给
最高效实现方案(调用标准库memcpy)
crt_memcpy是编译环境自带的标准库函数,本身已经做了SIMD、地址对齐等多层优化,拷贝几千字节数据的性能远高于手写的普通循环,正确写法如下:
; 定义源数组 .data array1 db 0CFh, 0C2h, 0ABh, 01Bh, 0C1h, 007h, 0F7h, 06Dh, 0DAh, 0F2h, 0DCh, 03Ch ; 剩余字节自行补全 ARRAY1_LEN equ $ - array1 ; 自动计算array1实际长度,无需手动统计 array2 db 0BFh, 03Dh, 087h, 0F1h, 0A6h, 097h, 0A6h, 05Bh, 010h, 0F2h, 051h, 05Eh ; 剩余字节自行补全 ARRAY2_LEN equ $ - array2 ; 预计算输出数组总长度,直接在静态段分配足够空间,省掉动态申请的开销 TOTAL_LEN equ ARRAY1_LEN + ARRAY2_LEN arrayOut db TOTAL_LEN dup(?) ; 直接分配对应长度的字节空间,无需单独定义指针 .code ; 拷贝第一个数组到输出数组起始位置 invoke crt_memcpy, offset arrayOut, offset array1, ARRAY1_LEN ; 拷贝第二个数组,目标地址为输出数组起始地址加第一个数组的长度 invoke crt_memcpy, offset arrayOut + ARRAY1_LEN, offset array2, ARRAY2_LEN ; 如果有更多数组,按偏移累加规则追加即可,例: ; invoke crt_memcpy, offset arrayOut + ARRAY1_LEN + ARRAY2_LEN, offset array3, ARRAY3_LEN
如果需要动态申请内存,不要直接用未初始化的指针,先申请内存拿到有效地址再拷贝:
.data? arrayOut dd ? ; 用于存储动态申请到的内存地址 .code invoke crt_malloc, TOTAL_LEN mov arrayOut, eax ; 拿到有效堆地址后再执行拷贝 invoke crt_memcpy, arrayOut, offset array1, ARRAY1_LEN invoke crt_memcpy, dword ptr [arrayOut] + ARRAY1_LEN, offset array2, ARRAY2_LEN ; 内存使用完后记得调用crt_free释放
手写循环正确实现(性能略低于标准库,但逻辑正确)
如果要自行实现拷贝逻辑,不要做多余的符号扩展,直接按字节搬移即可。性能最高的手写方式是用带硬件加速的串操作指令:
; 拷贝array1到输出数组起始位置 mov esi, offset array1 mov edi, offset arrayOut mov ecx, ARRAY1_LEN cld ; 清空方向标志位,设置地址从低到高增长 rep movsb ; 硬件加速按字节拷贝ecx个字节,性能远高于普通逐字节循环 ; 此时edi已经停在array1拷贝结束的位置,直接接续拷贝array2即可 mov esi, offset array2 mov ecx, ARRAY2_LEN rep movsb
如果要写最朴素的逐字节循环,注意不要搞多余的类型扩展,保证读写宽度匹配:
mov esi, 0 mov ecx, ARRAY1_LEN copy_arr1: mov al, array1[esi] mov arrayOut[esi], al inc esi loop copy_arr1 mov edi, 0 mov ecx, ARRAY2_LEN copy_arr2: mov al, array2[edi] mov arrayOut[esi + edi], al inc edi loop copy_arr2
性能注意事项
- 数组长度超过64字节的场景,不要自己写朴素逐字节循环,直接用
crt_memcpy或者rep movsb,现代CPU对这两种拷贝逻辑有专门的硬件优化,带宽可以跑到内存上限 - 拷贝的源地址、目标地址尽量按16/32字节对齐,可以进一步提升拷贝效率
- 单字节数组不要做符号扩展多字节写入,既浪费内存空间、拖慢拷贝速度,还会打乱内存布局
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

