可变参数函数

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();

}

可变参数分析:

  1. 首先要包含stdarg.h的头文件,因为里面包含处理可变参数函数的宏
  2. 可变参数函数的定义中至少定义一个参数,…代表可变参数。这个参数用于告诉编译器该函数还要传递的参数的个数,而且该参数在堆栈中的地址可用于定位其他参数的位置(因为上一个博客中已经得出这些参数是按顺序压入栈中的,而且存储单元是紧挨着的)。
  3. 可变参数函数读取实参的步骤:
    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:   }

猜你喜欢

转载自blog.csdn.net/thriveluo/article/details/47066191