C程序例子:为了了解可变参数函数汇中,参数读取的具体步骤,在add函数中本别用三个变量a,b,c来读取对应的参数。但在应用中,可用for循环,循环num次读取参数。
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>//包含了va_list等宏,用于处理可变参数的函数
//typedef char* valist; char指针类型
int add(int num,...)//int num 告诉编译器函数有多少个参数,...代表参数可变
{
int result = 0;
int a,b,c;
va_list argp;//typedef char * va_list; 创建一个指针
va_start(argp,num);//开始读取num个参数,读取结束后在把地址放在argp中
a = va_arg(argp,int);//va_arg()读取参数
b = va_arg(argp,int);
c = va_arg(argp,int);
va_end(argp);
return a+b+c;
}
void main()
{
int x = 3;
int r;
r = add(3,1,3,3);//
printf("%d\n",r);
getchar();
}
可变参数分析:
- 首先要包含stdarg.h的头文件,因为里面包含处理可变参数函数的宏
- 可变参数函数的定义中至少定义一个参数,…代表可变参数。这个参数用于告诉编译器该函数还要传递的参数的个数,而且该参数在堆栈中的地址可用于定位其他参数的位置(因为上一个博客中已经得出这些参数是按顺序压入栈中的,而且存储单元是紧挨着的)。
可变参数函数读取实参的步骤:
1). va_list argp;//typedef char * va_list; 创建一个指针;PS:va = variable argument,创建的 指针共va_start和va_end使用
2).va_start(argp,num);//开始读取参数,argp指向除num外的最左边的参数
a =va_arg(argp,int);//按整数类型,从对应的堆栈中读取参数,argp+=4,argp指向b;
b = va_arg(argp,int);//按整数类型,从对应的堆栈中读取参数,argp+=4,argp指向c;
c =va_arg(argp,int);//按整数类型,从对应的堆栈中读取参数,argp+=4,;
va_end(argp);//结束读取,argp = 0;
以下为对应反汇编程序及相应的注释
r = add(3,1,3,3);//
004010CF push 3 //地址:0018FEEC
004010D1 push 3 //地址:0018FEE8
004010D3 push 1 //地址:0018FEE4
004010D5 push 3 //地址:0018FEE0
004010D7 call @ILT+0(_add) (00401005)//函数返回地址0018FEDC
@ILT+0(_add):
00401005 jmp add (00401020)
int add(int num,...) //int num 告诉编译器函数有多少个参数,...代表参数可变
8: {
00401020 push ebp //地址:0018FED8
00401021 mov ebp,esp //此时ebp = 0018FED8
00401023 sub esp,54h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-54h]
0040102C mov ecx,15h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
9: int result = 0.0;
00401038 mov dword ptr [ebp-4],0
10: int a,b,c;
11: va_list argp; //typedef char * va_list; 创建一个指针
12:
13: va_start(argp,num); //开始读取num个参数,读取结束后在把地址放在argp中
0040103F lea eax,[ebp+0Ch] //0018FEE4 1的地址
00401042 mov dword ptr [ebp-14h],eax //把1的地址即出num外的左边的第一个参数的地址赋值给argp
14: a = va_arg(argp,int);
00401045 mov ecx,dword ptr [ebp-14h]
00401048 add ecx,4 //0018FEE8 2的地址,总是按int类型指向下一个单元
0040104B mov dword ptr [ebp-14h],ecx
0040104E mov edx,dword ptr [ebp-14h]
00401051 mov eax,dword ptr [edx-4]
00401054 mov dword ptr [ebp-8],eax //读取0018FEE4 即1的值
15: b = va_arg(argp,int);
00401057 mov ecx,dword ptr [ebp-14h]
0040105A add ecx,4 //0018FEEC 3的地址,总是按int类型指向下一个单元
0040105D mov dword ptr [ebp-14h],ecx
00401060 mov edx,dword ptr [ebp-14h]
00401063 mov eax,dword ptr [edx-4]
00401066 mov dword ptr [ebp-0Ch],eax //读取0018FEE8 即1的值
16: c = va_arg(argp,int);
00401069 mov ecx,dword ptr [ebp-14h]
0040106C add ecx,4 //0018FEF0 ,总是按int类型指向下一个单元
0040106F mov dword ptr [ebp-14h],ecx
00401072 mov edx,dword ptr [ebp-14h]
00401075 mov eax,dword ptr [edx-4]
00401078 mov dword ptr [ebp-10h],eax
17: va_end(argp);
0040107B mov dword ptr [ebp-14h],0 //结束参数读取,对argp重新赋值
18: return a+b+c;
00401082 mov eax,dword ptr [ebp-8]
00401085 add eax,dword ptr [ebp-0Ch]
00401088 add eax,dword ptr [ebp-10h]
19:
20: }