王爽-汇编语言-综合研究五-函数接收不定量参数
(一) 研究目的
我们知道,在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函数。