王爽-汇编语言-综合研究五-函数接收不定量参数

时间:2022-01-27 01:25:33

 

(一) 研究目的

我们知道,在C语言中,函数是可以传递参数的。有些函数在声明是就定义了要传的参数的个数,比如我们定义void a(int i);这说明函数a只接受一个int型参数。而有些函数,比如print函数,是可以接收不定个数的参数的。那函数是怎样接收不定量参数的呢?

(二) 研究过程

1) 有限个数的参数

首先我们来看程序是如何传参数的。我们编写一个程序,让他传递有限个参数:

王爽-汇编语言-综合研究五-函数接收不定量参数

我们编译链接,然后反汇编查看其代码:

王爽-汇编语言-综合研究五-函数接收不定量参数王爽-汇编语言-综合研究五-函数接收不定量参数

我们看其代码,首先,在main函数中,分别将‘a’与2对应的ASCLL码放到了Al中,然后PUSH AX入栈,在这里,放入,入栈这个过程进行了两次,入栈的数据就是我们的参数。然后调用子函数。子函数中。第一条语句是PUSH BP,这条语句很重要。然后我们看,MOV AL,[BP+04]。那么放到AL中的是什么数据呢?

我们分析:假设程序一开始ss:sp指向的是0005这个位置,那么,我们从开始到现在入栈三次后,栈内的空间如下分布。而BP的默认段地址寄存器也是SS,在子程序的开始SP的值给了BP。那么也就是说SS:BP指向的也是现在SS:SP指向的位置。那么,向AL中放入的数据是不是就是第一次PUSH的数据,也就是我们的第一个参数。

王爽-汇编语言-综合研究五-函数接收不定量参数

我们单步验证:

王爽-汇编语言-综合研究五-函数接收不定量参数王爽-汇编语言-综合研究五-函数接收不定量参数

我们看到,两次向AL中移动的就是我们传入的参数。由此我们可以知道,在C语言中参数的传递是通过栈来进行的。在函数调用前,将参数放入AX中,AX入栈,进入调用函数后,先把栈中的值出栈到AX中。这样就完成了函数间参数值的传递工作。

而且我们还发现这样两个现象:首先,在参数入栈的时候,程序首先入栈的是后面的参数,即入栈的时候参数是倒序入栈,这是由于栈的特性,出栈时就变成正序出栈。第二,在程序中,并没有出现类似参数个数的值。

2) 不定参数个数的函数

我们编写一个不定参数个数的函数如下:

王爽-汇编语言-综合研究五-函数接收不定量参数

我们编译连接之后反汇编如下:

王爽-汇编语言-综合研究五-函数接收不定量参数王爽-汇编语言-综合研究五-函数接收不定量参数王爽-汇编语言-综合研究五-函数接收不定量参数王爽-汇编语言-综合研究五-函数接收不定量参数王爽-汇编语言-综合研究五-函数接收不定量参数

3) 研究printf函数

上一个程序,我们给函数参数中传递了参数的个数,在程序中通过这个数来控制对参数的操作。但是printf的格式中,并没有传递类似的变量。那么printf是通过什么来得知和控制变量个数的呢。

我们写一个printf函数:

王爽-汇编语言-综合研究五-函数接收不定量参数

反汇编如下:

王爽-汇编语言-综合研究五-函数接收不定量参数

这是main函数。我们看到这里调用了0AC5这个子程序。我们分析,这是在做完准备工作后进入了printf函数的子程序。而且,我们看到,“%c%d”这个参数,放入栈中的时候,被翻译成了0194。

我们知道,函数中,两个参数之间用逗号分隔,也就是说“%c%d”应该是按照一个参数传递进去的。也就是说,它应该是被认为是一个字符串。“%c%d”可能不是很清晰。我们编写一个更多参数的peintf函数。

王爽-汇编语言-综合研究五-函数接收不定量参数

其代码如下:

王爽-汇编语言-综合研究五-函数接收不定量参数

这里传入的第一个参数值仍然为0194。这个值有可能是字符串存放的地址。我们查看:

王爽-汇编语言-综合研究五-函数接收不定量参数

我们看到这里存放的果然是这个字符串。

也就是说,我们完全可以借鉴这种机制,看字符串的长度,来确定参数的个数。

4) 我们自己的printf函数

首先我们要确定我们的函数传递参数的方式,我们要传递无个数限制的参数的,而且我们是通过第一个字符串来确定我们参数的个数的。我们根据以上的研究结果定义一个函数如下:

void print(char * str,...);

main()

{

print("%c %c %d %d",'a','b','c','e');

}

void print(char * str,...)

{

int i=0;

int j=0;

int sj=0;

int js=0;

int dx[200];

int color=2;

char ch=str[i++];

int wz=2;

while(ch)

{

if(ch=='%')

{

ch=str[i++];

if(ch=='c')

{

*(char far *)(0xb8000000+wz)=*(int *)(_BP+6+j);

*(char far *)(0xb8000001+wz)=color;

wz=wz+2;

j++;

}

if(ch=='d')

{

js=0;

sj=*(int*)(_BP+6+j);

j++;

if(sj==0)

{

*(char far*)(0xb8000000+wz)='0';

*(char far*)(0xb8000001+wz)=color;

wz=wz+2;

}

else

{

while(sj!=0)

{

dx[js]=sj%10;

sj=sj/10;

js++;

}

js--;

for(;js>=0;js--)

{

*(char far *)(0xb8000000+wz)=dx[js]+48;

*(char far *)(0xb8000001+wz)=color;

wz=wz+2;

}

}

}

j++;

}

else

{

*(char far *)(0xb8000000+wz)=ch;

*(char far *)(0xb8000001+wz)=color;

wz=wz+2;

}

ch=str[i++];

}

}

我们看到运行得结果如下

王爽-汇编语言-综合研究五-函数接收不定量参数

这样,我们就写出了一个简单的printf函数。