首页 > 汇编语言 > 汇编语言MS-Windows编程
阅读:4,412
汇编语言动态内存分配(堆分配)
动态内存分配 (dynamic memory allocation),又被称为堆分配 (heap allocation),是编程语言使用的一种技术,用于在创建对象、数组和其他结构时预留内存。比如在 Java 语言中,下面的语句就会为 String 对象保留内存:
当接收到一个分配请求时,堆管理器就把适当大小的内存块标识为已预留,并返回指向该块的指针。之后,当接收到对同一个块的删除请求时,堆就释放该内存块,并将其返回到空闲列表。每次接收到新的分配请求,堆管理器就会扫描空闲列表,寻找第一个可用的、且容量足够大的内存块来响应请求。
汇编语言程序有两种方法进行动态分配:
利用下表中的几个 Win32 API 函数就可以从 Windows 中请求多个不同大小的内存块。表中所有的函数都会覆盖通用寄存器,因此程序可能想要创建封装过程来实现重要寄存器的入栈和出栈操作。
如果 HeapAlloc 成功,则 EAX 包含指向新存储区的指针;如果失败,则 EAX 中的返回值为 NULL。下面的代码用 hHeap 标识一个堆,从该堆中分配了一个 1000 字节的数组,并将数组初始化为全零:
示例调用如下:
HeapCreate 调用示例如下:
String str = new String("abcde");
同样的,在 C++ 中,对变量使用大小属性就可以为一个整数数组分配空间:
int size;
cin >> size; //用户输入大小
int array[] = new int[size];
当接收到一个分配请求时,堆管理器就把适当大小的内存块标识为已预留,并返回指向该块的指针。之后,当接收到对同一个块的删除请求时,堆就释放该内存块,并将其返回到空闲列表。每次接收到新的分配请求,堆管理器就会扫描空闲列表,寻找第一个可用的、且容量足够大的内存块来响应请求。
汇编语言程序有两种方法进行动态分配:
- 方法一:通过系统调用从操作系统获得内存块。
- 方法二:实现自己的堆管理器来服务更小的对象提出的请求。
利用下表中的几个 Win32 API 函数就可以从 Windows 中请求多个不同大小的内存块。表中所有的函数都会覆盖通用寄存器,因此程序可能想要创建封装过程来实现重要寄存器的入栈和出栈操作。
函数 | 描述 |
---|---|
GetProcessHeap | 用 EAX 返回程序现存堆区域的 32 位整数句柄。如果函数成功,则 EAX 中的返回值为堆句柄。 如果函数失败,则 EAX 中的返回值为 NULL |
HeapAlloc | 从堆中分配内存块。如果成功,EAX 中的返回值就为内存块的地址。如果失败,则 EAX 中的返 回值为 NULL |
HeapCreate | 创建新堆,并使其对调用程序可用。如果函数成功,则 EAX 中的返回值为新创建堆的句柄。如果失败,则 EAX 的返回值为 NULL |
HeapDestroy | 销毁指定堆对象,并使其句柄无效。如果函数成功,则 EAX 中的返回值为非零 |
HeapFree | 释放之前从堆中分配的内存块,该堆由其地址和堆句柄进行标识。如果内存块释放成功,则返回值为非零 |
HeapReAlloc | 对堆中内存块进行再分配和调整大小。如果函数成功,则返回值为指向再分配内存块的指针。如果函数失败,且没有指定 HEAP GENERATE EXCEPTIONS,则返回值为 NULL |
HeapSize | 返回之前通过调用 HeapAlloc 或 HeapReAlloc 分配的内存块的大小。如果函数成功,则 EAX 包含被分配内存块的字节数。如果函数失败,则返回值为 SIZE_T-1 ( SIZE_T 等于指针能指向的最大字节数 ) |
GetProcessHeap
如果使用的是当前程序的默认堆,那么 GetProcessHeap 就足够了。这个函数没有参数,EAX 中的返回值就是堆句柄:GetProcessHeap PROTO
示例调用:.data hHeap HANDLE ? .code INVOKE GetProcessHeap .IF eax == NULL ;不能获取句柄 jmp quit .ELSE mov hHeap,eax ;句柄 ok .ENDIF
HeapCreate
HeapCreate 能为当前程序创建一个新的私有堆:
HeapCreate PROTO,
flOptions:DWORD, ;堆分配选项
dwInitialSize:DWORD, ;按字节初始化堆大小
dwMaximumSize:DWORD ;最大堆字节数
HEAP_START = 2000000 ; 2 MB HEAP_MAX = 400000000 ; 400 MB .data hHeap HANDLE ? ; 堆句柄 .code INVOKE HeapCreate, 0, HEAP_START, HEAP_MAX .IF eax == NULL ; 堆未创建 call WriteWindowsMsg ; 显示错误消息 jmp quit .ELSE mov hHeap,eax ; 句柄 OK .ENDIF
HeapDestroy
HeapDeatroy 销毁一个已存在的私有堆(由 HeapCreate 创建)。需向其传递堆句柄:
HeapDestroy PROTO,
hHeap:DWORD ;堆句柄
.data hHeap HANDLE ? ;堆句柄 .code INVOKE HeapDestroy, hHeap .IF eax == NULL call WriteWindowsMsg ;显示错误消息 .ENDIF
HeapAlloc
HeapAlloc 从已存在堆中分配一个内存块:
HeapAlloc PROTO,
hHeap:HANDLE, ;现有堆内存块的句柄
dwFlags :DWORD, ;堆分配控制标志
dwBytes:DWORD ;分配的字节数
- hHeap:32 位堆句柄,该堆由 GetProcessHeap 或 HeapCreate 初始化。
- dwFlags:一个双字,包含了一个或多个标志值。可以选择将其设置为 HEAP_ZERO_MEMORY,即设置内存块为全零。
- dwBytes:一个双字,表示堆分配的字节数。
如果 HeapAlloc 成功,则 EAX 包含指向新存储区的指针;如果失败,则 EAX 中的返回值为 NULL。下面的代码用 hHeap 标识一个堆,从该堆中分配了一个 1000 字节的数组,并将数组初始化为全零:
.data hHeap HANDLE ? ;堆句柄 pArray DWORD ? ;数组指针 .code INVOKE HeapAlloc, hHeap, HEAP_ZERO_MEMORY, 1000 .IF eax == NULL mWrite "HeapAlloc failed" jmp quit .ELSE mov pArray,eax .ENDIF
HeapFree
函数 HeapFree 释放之前从堆中分配的一个内存块,该堆由其地址和堆句柄标识:
HeapFree PROTO,
hHeap:HANDLE,
dwFlags:DWORD,
lpMem:DWORD
示例调用如下:
INVOKE HeapFree, hHeap, 0, pArray
Error Handling
若在调用 HeapCreate、HeapDestroy 或 GetProcessHeap 时遇到错误,可以通过调用 API 函数 GetLastError 来获得详细信息。还可以调用 Irvine32 链接库的函数 WriteWindowsMsg。HeapCreate 调用示例如下:
INVOKE HeapCreate, 0, HEAP_START, HEAP_MAX .IF eax == NULL ;失败? call WriteWindowsMsg ;显示错误信息 .ELSE mov hHeap,eax ;成功 .ENDIF反之,函数 HeapAlloc 在失败时不会设置系统错误码,因此也就无法调用 GetLastError 或 WriteWindowsMsg。