zoukankan      html  css  js  c++  java
  • C++可变参数的另一种实现

    大家熟知的C库函数printf函数就是一个可变参数函数,它是怎么实现的呢?不过他实现是有条件的,必须函数参数的入栈顺序为从右向左的顺序,也即函数的形参,在函数调用之前,必须是最右边的参数先入栈,并且参数都必须通过栈传递,以1个例子说明,如函数func(arg1, arg2,arg3),那么函数的堆栈应是:

                                   

    ebp是帧指针寄存器,一般用来存取堆栈,有了堆栈结构,下面我们看看C可变参数的具体实现原理:

    [html] view plain copy
     
    1. #include <stdio.h>  
    2. enum {  
    3.         ptChar,  
    4.         ptInt,  
    5.         ptFloat,  
    6.         ptDouble,  
    7.     };  
    8. void printSum(unsigned long paramFormat, ...)  
    9. {  
    10.     /*高16位为可变参数类型,低16位为可变参数个数*/  
    11.     int paramType = (paramFormat >> 16);  
    12.     int paramNum = paramFormat & 0xffff;  
    13.       
    14.     /*¶mFormat = ebp + 8,第一个参数的地址*/  
    15.     unsigned long *pArg = ¶mFormat;  
    16.     /*ebp + 0x0c, 第二个参数地址*/  
    17.     pArg++;  
    18.     switch(paramType)  
    19.     {  
    20.     case ptChar:  
    21.         {  
    22.             int sum = 0;  
    23.             for (int i = 0; i paramNum; i++)  
    24.             {  
    25.                 char *pValue = (char *)pArg;  
    26.                 sum += *pValue;  
    27.                 pArg++;  
    28.             }  
    29.             printf("%d ", sum);  
    30.         }  
    31.         break;  
    32.     case ptInt:  
    33.         {  
    34.             int sum = 0;  
    35.             for (int i = 0; i paramNum; i++)  
    36.             {  
    37.                 int *pValue = (int *)pArg;  
    38.                 sum += *pValue;  
    39.                 pArg++;  
    40.             }  
    41.             printf("%d ", sum);  
    42.         }  
    43.         break;  
    44.     case ptFloat:  
    45.         {  
    46.             float sum = 0;  
    47.             /**/  
    48.             pArg++;  
    49.   
    50.             /*浮点参数,堆栈占8个字节,所以指针偏移为8*/  
    51.             for (int i = 0; i paramNum; i++)  
    52.             {  
    53.                 float *pValue = (float *)pArg;  
    54.                 sum += *pValue;  
    55.                 pArg++;  
    56.                 pArg++;  
    57.             }  
    58.             printf("%f ", sum);  
    59.         }  
    60.         break;  
    61.         case ptDouble:  
    62.         {  
    63.             double sum = 0;  
    64.             /*双精度浮点参数,堆栈占8个字节,所以指针偏移为8*/  
    65.             for (int i = 0; i paramNum; i++)  
    66.             {  
    67.                 double *pValue = (double *)pArg;  
    68.                 sum += *pValue;  
    69.                 pArg++;  
    70.                 pArg++;  
    71.             }  
    72.             printf("%f ", sum);  
    73.         }  
    74.         break;  
    75.     default:  
    76.         printf("unknowned type! ");  
    77.         break;  
    78.     }  
    79. }  
    80.   
    81. void main()  
    82. {  
    83.     unsigned long paramFormat = 3;  
    84.     char a = 1, b = 2, c = 3;  
    85.     printSum(paramFormat, a, b, c);  
    86.   
    87.   
    88.     paramFormat = ptInt <16;  
    89.     paramFormat += 3;  
    90.     int ia = 1, ib = 2, ic = 3;  
    91.     printSum(paramFormat, ia, ib, ic);  
    92.   
    93.     paramFormat = ptFloat <16;  
    94.     paramFormat += 3;  
    95.     float fa = 1, fb = 2, fc = 3;  
    96.     printSum(paramFormat, fa, fb, fc);  
    97.   
    98.     paramFormat = ptDouble <16;  
    99.     paramFormat += 3;  
    100.     double da = 1, db = 2, dc = 3;  
    101.     printSum(paramFormat, da, db, dc);  
    102. }  

    上面这种方法对函数参数的入栈顺序有限制,必须从右向左入栈,这就是为什么pascal调用方式不能实现printf的原因,并且函数形参都要通过栈来传递,这对有些编译器为了优化处理,函数参数通过寄存器来传递,从而不满足要求。鉴于次,本文采用C++的默认形参实现可变参数的方法,没有上面的这些限制,下面是实现代码:

    [html] view plain copy
     
    1. #include <stdio.h>  
    2. enum {  
    3.         ptChar,  
    4.         ptInt,  
    5.         ptFloat,  
    6.         ptDouble,  
    7.     };  
    8. void printSum(unsigned long paramType,  
    9.               void *arg1 = NULL,  
    10.               void *arg2 = NULL,  
    11.               void *arg3 = NULL,  
    12.               void *arg4 = NULL,  
    13.               void *arg5 = NULL,  
    14.               void *arg6 = NULL,  
    15.               void *arg7 = NULL,  
    16.               void *arg8 = NULL,  
    17.               void *arg9 = NULL,  
    18.               void *arg10 = NULL)  
    19. {  
    20.     void *arg[10] = {  
    21.         arg1,  
    22.         arg2,  
    23.         arg3,  
    24.         arg4,  
    25.         arg5,  
    26.         arg6,  
    27.         arg7,  
    28.         arg8,  
    29.         arg9,  
    30.         arg10,  
    31.     };  
    32.     switch(paramType)  
    33.     {  
    34.     case ptChar:  
    35.         {  
    36.             int sum = 0;  
    37.             for (int i = 0; i 10; i++)  
    38.             {  
    39.                 if (arg[i] != NULL)  
    40.                 {  
    41.                     char *pValue = (char *)arg[i];  
    42.                     sum += *pValue;  
    43.                 }  
    44.                 else  
    45.                     break;  
    46.             }  
    47.             printf("%d ", sum);  
    48.         }  
    49.         break;  
    50.     case ptInt:  
    51.         {  
    52.             int sum = 0;  
    53.             for (int i = 0; i 10; i++)  
    54.             {  
    55.                 if (arg[i] != NULL)  
    56.                 {  
    57.                     int *pValue = (int *)arg[i];  
    58.                     sum += *pValue;  
    59.                 }  
    60.                 else  
    61.                     break;  
    62.             }  
    63.             printf("%d ", sum);  
    64.         }  
    65.         break;  
    66.     case ptFloat:  
    67.         {  
    68.             float sum = 0;  
    69.             for (int i = 0; i 10; i++)  
    70.             {  
    71.                 if (arg[i] != NULL)  
    72.                 {  
    73.                     float *pValue = (float *)arg[i];  
    74.                     sum += *pValue;  
    75.                 }  
    76.                 else  
    77.                     break;  
    78.             }  
    79.             printf("%f ", sum);  
    80.         }  
    81.         break;  
    82.         case ptDouble:  
    83.         {  
    84.             double sum = 0;  
    85.             for (int i = 0; i 10; i++)  
    86.             {  
    87.                 if (arg[i] != NULL)  
    88.                 {  
    89.                     double *pValue = (double *)arg[i];  
    90.                     sum += *pValue;  
    91.                 }  
    92.                 else  
    93.                     break;  
    94.             }  
    95.             printf("%f ", sum);  
    96.         }  
    97.         break;  
    98.     default:  
    99.         printf("unknowned type! ");  
    100.         break;  
    101.     }  
    102. }  
    103.   
    104. void main()  
    105. {  
    106.     unsigned long paramType = ptChar;  
    107.     char a = 1, b = 2, c = 3;  
    108.     printSum(paramType, &a, &b, &c);  
    109.   
    110.     paramType = ptInt;  
    111.     int ia = 1, ib = 2, ic = 3;  
    112.     printSum(paramType, &ia, &ib, &ic);  
    113.   
    114.     paramType = ptFloat;  
    115.     float fa = 1, fb = 2, fc = 3;  
    116.     printSum(paramType, &fa, &fb, &fc);  
    117.   
    118.     paramType = ptDouble;  
    119.     double da = 1, db = 2, dc = 3;  
    120.     printSum(paramType, &da, &db, &dc);  
    121. }  

    http://blog.csdn.net/rabinsong/article/details/8946514

  • 相关阅读:
    一、HTML基础学习
    算法之求正整数的二进制
    JavaScript ajax返回状态
    ajax实现异步校验
    比较喜欢的一种求阶乘的方法:用递归求阶乘
    java中自定义异常类
    我对多态的理解
    包装类和基本类型区别?(integer和int取值范围一样大)
    JAVA中默认的编码方式
    抽象类和接口的区别
  • 原文地址:https://www.cnblogs.com/findumars/p/5615727.html
Copyright © 2011-2022 走看看