Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1613047
  • 博文数量: 441
  • 博客积分: 20087
  • 博客等级: 上将
  • 技术积分: 3562
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-19 15:35
文章分类

全部博文(441)

文章存档

2014年(1)

2012年(1)

2011年(8)

2010年(16)

2009年(15)

2008年(152)

2007年(178)

2006年(70)

分类: C/C++

2009-01-19 22:53:44

利用嵌入汇编代码的方法,计算n个整数的平均值,下面是代码:



// avg.c

#include <stdio.h>

int avg(int num, ...);

int main()
{
    int result;
    result = avg(5, 2, 8, 7, 4, 9);
    printf("avg is %d\n", result);
    return 0;
}

int avg(int num, ...)
{
    int i = 0, sum = 0;
    for ( i = 0; i < num; i++ )
    __asm
    {
        ; eax = (3 + i) * 4
        mov eax, i
        add    eax, 3
        shl eax, 2        
        mov ebx, [ebp + eax]
        add sum, ebx
    }
    
    return (int)(sum / num);
}


int avg(int num, …); 这个函数返回num(num>=1)个整数的平均值,

很显然,它的调用约定必须为__cdecl main函数调用它的时候, 需要main函数自己平衡堆栈,嵌入的汇编代码用来计算这num个整数的总和。

 

下面我们来分析一下这个程序的主要执行过程:

 

main定义了一个临时变量result用来保存平均值,即avg函数的返回值。

在进入这个avg函数之前,main函数需要传递所有的变量进去:

push 9

push 4

push 7

push 8

push 2

push 5

此时的栈空间如下:

 

 

 

 

 

5

=> esp

2

 

8

 

7

 

4

 

9

 

……

 

 

__cdecl调用方式参数传递是从右到左。当参数全部传入进去之后,就会调用这个函数:

0040D7B4   call        @ILT+5(_avg) (0040100a)   调用函数

0040D7B9   add         esp,18h                  平衡堆栈

 

call指令执行的时候,call的返回地址会被压入栈:

 

 

….

 

Call返回地址

=> esp

5

 

2

 

8

 

7

 

4

 

9

 


avg函数内部, 首先ebp的值会被压入栈保存,并且esp当前的值会被送入到ebp

push ebp

mov ebp, esp


….

 

Ebp的值

Esp  <= ebp指向这里

Call返回地址

Ebp + 4

5

Ebp + 8

2

Ebp + 12

8

Ebp + 16

7

Ebp + 20

4

Ebp + 24

9

Ebp + 28


于是我们就可以通过ebp加上一个偏移来访问被main函数入栈的参数

ebp + 4call调用的返回地址, ebp + 8 则是第一个参数num(这里是5)值,后面的num个就是我们要获取的整数,如果用个循环,

for ( i=0;i<5;i++), 那么我们要获取的参数的将是[ebp+(3+i)*4], 这种写法

mov ebx, [ebp+(3+i)*4]虽然在VC6下可以编译通过, 但是是错误的, 正确的代码如下:


; eax = (3+i)*4

mov eax, i             ; i的值送入eax

add eax, 3             ; eax = 3 + i

shl eax, 2             ; eax = (3+i)*4

mov ebx, [ebp +eax]    ; [ebp + (3+i)*4]的内容,即每个参数,送入ebx

add sum, ebx           ; 将结果累加起来,放在临时变量sum

 

我们利用ebp寄存器,非常轻易的访问了所有被压入栈的参数,这比直接使用c语言要简洁高效。但我们需要注意一点的是,必须非常清楚各个参数被压入栈的顺序,以及ebp寄存器当前指向的位置。

 

关于调用约定问题,可以参考这篇文章:

调用方式__cdecl和__stdcall的异同点

关于本文的纯粹c语言实现方式,可以参考这篇文章:
c语言实现求不定参数平均值

author: thinker
e-mail: cnhnyugmail.com
qq: 94483026

阅读(1367) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~