MASM环境下DbRecord动态数组addElement函数修复问询
问题描述
我要实现一个存储DbRecord结构体指针的动态数组,arrayPtr指向数组首元素,支持动态添加新元素并维护数组。计划用VirtualAlloc分配内存、RtlMoveMemory复制数据,但addElement函数无法正确将新元素加入数组。初始版本里对RtlMoveMemory的用法存疑,更新版本调整了该函数的参数,但不知道怎么把VirtualAlloc返回的内存地址存入arrayPtr的对应位置(arrayPtr + arrayCount * sizeof DbRecord)。
初始版本代码
.386 .model flat, stdcall option casemap :none include windows.inc include user32.inc include kernel32.inc addElement PROTO: ptr DbRecord .data? DbRecord struct Id dd ? WordOne db 32 dup(?) ; db is define byte, set value of byte WordTwo db 32 dup(?) WordThree db 32 dup(?) Year dd ? DbRecord ends arrayPtr dd ? ; pointer in memory to start of array newElementPointer DbRecord <> hStdOut dd ? bytesWritten dd ? .data arrayCount dd 0 hello db 'Hello World!', 0 .code main proc LOCAL DbRecord01:DbRecord mov [DbRecord01.Id], 1; ; any other way than one character at a time? mov byte ptr [DbRecord01.WordOne], 'D' mov byte ptr [DbRecord01.WordOne + 1], 'o' mov byte ptr [DbRecord01.WordOne + 2], 'g' mov byte ptr [DbRecord01.WordOne + 3], 0 mov byte ptr [DbRecord01.WordTwo], 'C' mov byte ptr [DbRecord01.WordTwo + 1], 'a' mov byte ptr [DbRecord01.WordTwo + 2], 't' mov byte ptr [DbRecord01.WordTwo + 3], 0 mov byte ptr [DbRecord01.WordThree], 'E' mov byte ptr [DbRecord01.WordThree + 1], 'y' mov byte ptr [DbRecord01.WordThree + 2], 'e' mov byte ptr [DbRecord01.WordThree + 3], 0 mov [DbRecord01.Year], 2022; invoke GetStdHandle, STD_OUTPUT_HANDLE mov [hStdOut], eax invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL invoke addElement, addr DbRecord01 ret main endp addElement proc DbRecordPointer: ptr DbRecord invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE ; I beleive store a memory address in eax invoke RtlMoveMemory, DbRecord ptr [eax], DbRecordPointer, sizeof DbRecord ; but how to use that memory address here? ret addElement endp end main
更新版本代码
.386 .model flat, stdcall option casemap :none include windows.inc include user32.inc include kernel32.inc addElement PROTO: ptr DbRecord .data? DbRecord struct Id dd ? WordOne db 32 dup(?) ; db is define byte, set value of byte WordTwo db 32 dup(?) WordThree db 32 dup(?) Year dd ? DbRecord ends arrayPtr dword ? ; pointer in memory to start of array ; newElementPointer DbRecord <> hStdOut dd ? bytesWritten dd ? .data arrayCount dd 0 hello db 'Hello World!', 0 .code main proc LOCAL DbRecord01:DbRecord mov [DbRecord01.Id], 1; ; any other way than one character at a time? mov byte ptr [DbRecord01.WordOne], 'D' mov byte ptr [DbRecord01.WordOne + 1], 'o' mov byte ptr [DbRecord01.WordOne + 2], 'g' mov byte ptr [DbRecord01.WordOne + 3], 0 mov byte ptr [DbRecord01.WordTwo], 'C' mov byte ptr [DbRecord01.WordTwo + 1], 'a' mov byte ptr [DbRecord01.WordTwo + 2], 't' mov byte ptr [DbRecord01.WordTwo + 3], 0 mov byte ptr [DbRecord01.WordThree], 'E' mov byte ptr [DbRecord01.WordThree + 1], 'y' mov byte ptr [DbRecord01.WordThree + 2], 'e' mov byte ptr [DbRecord01.WordThree + 3], 0 mov [DbRecord01.Year], 2022; invoke GetStdHandle, STD_OUTPUT_HANDLE mov [hStdOut], eax invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL invoke addElement, addr DbRecord01 ret main endp addElement proc uses edx DbRecordPointer: ptr DbRecord Local newElementPointer: Dword invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE ; I beleive store a memory address in eax mov newElementPointer, eax ;invoke RtlMoveMemory, newElementPointer , DbRecordPointer, sizeof DbRecord ; but how to use that memory address here? invoke RtlMoveMemory, eax , DbRecordPointer, sizeof DbRecord mov edx, arrayCount inc edx mov arrayCount, edx ;mov dword ptr [arrayPtr+arrayCount], eax ret addElement endp end main
解决方案
要实现存储结构体指针的动态数组,核心是先管理数组本身的内存(存储指针的数组),再管理每个结构体实例的内存,具体修正步骤如下:
1. 修正addElement函数逻辑
核心步骤:
- 计算新数组所需内存:每次添加元素后,数组大小为
(arrayCount + 1) * sizeof DWORD(因为每个元素是4字节的指针) - 处理数组扩容:
- 如果是第一次添加元素(
arrayPtr为空),直接分配新数组内存 - 如果已有数组,重新分配更大的内存,复制旧数组内容,释放旧内存
- 如果是第一次添加元素(
- 分配新的
DbRecord内存并复制数据 - 将新结构体的指针存入数组的对应位置
- 更新数组元素计数
修正后的addElement代码
addElement proc uses ebx ecx edx DbRecordPointer: ptr DbRecord LOCAL newArrayPtr: DWORD LOCAL newRecordPtr: DWORD ; 1. 分配新的DbRecord内存并复制数据 invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE .if eax == NULL ret ; 内存分配失败,直接返回 .endif mov newRecordPtr, eax invoke RtlMoveMemory, newRecordPtr, DbRecordPointer, sizeof DbRecord ; 2. 处理数组的扩容或初始化 mov ecx, arrayCount inc ecx ; 新的元素数量 mov edx, sizeof DWORD mul edx ; eax = 新数组所需字节数 (ecx * 4) .if arrayPtr == NULL ; 第一次添加,直接分配数组内存 invoke VirtualAlloc, NULL, eax, MEM_COMMIT, PAGE_READWRITE .if eax == NULL invoke VirtualFree, newRecordPtr, 0, MEM_RELEASE ; 释放已分配的结构体内存 ret .endif mov newArrayPtr, eax .else ; 已有数组,重新分配更大的内存 invoke VirtualAlloc, NULL, eax, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE .if eax == NULL invoke VirtualFree, newRecordPtr, 0, MEM_RELEASE ret .endif mov newArrayPtr, eax ; 复制旧数组内容到新数组 mov edx, arrayCount mov ecx, sizeof DWORD mul edx ; eax = 旧数组字节数 invoke RtlMoveMemory, newArrayPtr, arrayPtr, eax ; 释放旧数组内存 invoke VirtualFree, arrayPtr, 0, MEM_RELEASE .endif ; 3. 将新结构体指针存入数组的对应位置 mov ebx, arrayCount mov edx, sizeof DWORD mul ebx ; eax = 偏移量 (arrayCount * 4) mov [newArrayPtr + eax], newRecordPtr ; 4. 更新数组指针和计数 mov arrayPtr, newArrayPtr mov arrayCount, ecx ret addElement endp
2. 额外优化:字符串赋值简化
原代码中逐个字节赋值字符串的方式可以用lstrcpy替换,更简洁:
invoke lstrcpy, addr DbRecord01.WordOne, addr 'Dog' invoke lstrcpy, addr DbRecord01.WordTwo, addr 'Cat' invoke lstrcpy, addr DbRecord01.WordThree, addr 'Eye'
关键注意事项
- 每次内存分配后必须检查返回值是否为
NULL,避免空指针访问 - 数组扩容时要记得释放旧内存,防止内存泄漏
- 因为数组存储的是结构体指针,所以数组的每个元素是
DWORD类型,偏移量计算要用arrayCount * sizeof DWORD,而不是sizeof DbRecord
内容的提问来源于stack exchange,提问作者Chad Botting
相关产品推荐
相关产品推荐

