C++可变参数的另一种实现

Posted 朝闻道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\\n", 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\\n", 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\\n", 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\\n", sum);  
  73.         }  
  74.         break;  
  75.     default:  
  76.         printf("unknowned type!\\n");  
  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\\n", 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\\n", 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\\n", 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\\n", sum);  
  96.         }  
  97.         break;  
  98.     default:  
  99.         printf("unknowned type!\\n");  
  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

以上是关于C++可变参数的另一种实现的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中做模板的另一种方法?

在 C++ 中使用 continue 关键字的另一种方法

Java Lock-同步的另一种实现

如何使用算法将一种类型的列表映射到现代 C++ 中的另一种类型的列表

[C++多线程]1.3-多线程控制的另一种姿势-条件变量(condition_variable), 信号量(semaphore)

[C++多线程]1.3-多线程控制的另一种姿势-条件变量(condition_variable), 信号量(semaphore)