4.2.4 x86: alloca() 函数
对alloca()函数并没有值得学习的。
该函数的作用像malloc()一样,但只会在栈上分配内存。
它分配的内存并不需要在函数结束时,调用像free()这样的函数来释放,当函数运行结束,ESP的值还原时,这部分内存会自动释放。对alloca()函数的实现也没有什么值得介绍的。
这个函数,如果精简一下,就是将ESP指针指向栈底,根据你所需要的内存大小将ESP指向所分配的内存块。让我们试一下:
#include <malloc.h>
#include <stdio.h>
void f() {
char *buf=(char*)alloca (600);
_snprintf (buf, 600, "hi! %d, %d, %d\n", 1, 2, 3);
puts (buf);
};
(snprintf()函数作用与printf()函数基本相同,不同的地方是printf()会将结果输出到的标准输出stdout,snprintf()会将结果保存到内存中,后面两⾏代码可以使用printf()替换,但我想说明小内存的使用习惯。)
MSVC
让我们来编译 (MSVC 2010):
...
 mov eax, 600 ; 00000258H
call __alloca_probe_16
mov esi, esp
 push 3
push 2
push 1
push OFFSET $SG2672
push 600 ; 00000258H
push esi
call __snprintf
 push esi
call _puts
add esp, 28 ; 0000001cH
...
这唯一的函数参数是通过EAX(未使用栈)传递。在函数调用结束时,ESP会指向 600字节的内存,我们可以像使用一般内存一样来使用它做为缓冲区。
GCC + Intel格式
GCC 4.4.1不需要调用函数就可以实现相同的功能:
.LC0:
.string "hi! %d, %d, %d\n"
f:
push ebp
mov ebp, esp
push ebx
sub esp, 660
lea ebx, [esp+39]
and ebx, -16 ; align pointer by 16-bit border
mov DWORD PTR [esp], ebx ; s
mov DWORD PTR [esp+20], 3
mov DWORD PTR [esp+16], 2
mov DWORD PTR [esp+12], 1
mov DWORD PTR [esp+8], OFFSET FLAT:.LC0 ; "hi! %d, %d, %d\n"
mov DWORD PTR [esp+4], 600 ; maxlen
call _snprintf
mov DWORD PTR [esp], ebx
call puts
mov ebx, DWORD PTR [ebp-4]
leave
ret
### GCC + AT&T 格式
我们来看相同的代码,但使用了AT&T格式:
.LC0:
.string "hi! %d, %d, %d\n"
f:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $660, %esp
leal 39(%esp), %ebx
andl $-16, %ebx
movl %ebx, (%esp)
movl $3, 20(%esp)
movl $2, 16(%esp)
movl $1, 12(%esp)
movl $.LC0, 8(%esp)
movl $600, 4(%esp)
call _snprintf
movl %ebx, (%esp)
call puts
movl -4(%ebp), %ebx
leave
ret
代码与上面的那个图是相同的。
例如:movl $3, 20(%esp)与mov DWORD PTR [esp + 20],3是等价的,Intel的内存地址增加是使用register+offset,而AT&T使用的是offset(%register)。
当前内容版权归 Dennis Yurichev 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 Dennis Yurichev .