写点什么

C++ 学习 --- 变长参数(stdarg.h)的实现原理

作者:桑榆
  • 2022 年 9 月 12 日
    广东
  • 本文字数:1189 字

    阅读完需:约 4 分钟

引用

C++ 中对 stdarg.h 头文件进行了封装,该头文件实现了函数变长参数,能够在定义函数时不必完全指定参数个数,而编译器能够在代码编译时,拿到所有的参数,并进行相应的处理。

stdarg.h 中定义了 va_list 类型,va_start/va_arg/va_end/va_copy4 个宏,我们具体探究一下其实现原理。

变长参数定义与原理

定义

变长参数是 C 语言中的特殊参数形式,函数声明如下:

int printf(const char* format, ...)这样的声明表示,该函数除了第一个参数类型为 const char*之外,后面可以追加任意数量和类型的参数;

在函数实现的过程中,我们使用 stdarg.h 中的宏来依次访问后续额外的参数。

使用方式

假设变长参数函数的最后一个具名参数(如上面的 format)为 lastarg,那么我们需要在函数实现中定义 va_list 类型的变量如下:

valist ap;

该变量后续将会指向每一个未知参数,首先我们需要使用 va_start 对 ap 进行初始化,注意,这里会用到 lastarg:

va_start(ap, lastarg);

然后,我们使用 va_arg 来依次获取下一个不确定的参数,假设该参数的类型为 T:

T next = va_arg(ap, T);

在函数结束之前,我们使用 va_end 清理现场:

va_end(ap)

实现原理

C 语言中默认的 cdecl 调用惯例是自右往左压栈传递函数参数,例如函数(后面跟着 count 个整数,计算它们的和)

int sum(int count,...);

那么对应调用sum(3,1,2,5),此时,在栈上面地址由低到高是 3,1,2,5,这样,我们知道了第一个参数 count 的地址,那么也能够依次获知上面三个参数的地址,最后计算出结果:

int sum(int count,...){    int* p = &count + 1;    int ret_sum = 0;    while(count--)        ret_sum += *p++;    return ret_sum;}
复制代码

但是,实际上的过程中,后续每一个参数的类型都不一样,我们需要增加的地址数也不一样,所以我们需要进行改进,使用 void 或者 char 指针替代明确的指针 int*。

  • va_list 是一个指针,选择 void 或 char;

  • va_start 将 va_list 指向最后一个具名参数后面的位置,即第一个不确定参数的位置;

  • va_arg 获取当前参数的值,同时将指针指向下一个参数;

  • va_end 将指针置为 0。

所以,我们的宏这样定义:

#define va_list char*#define va_start(ap,arg) (ap=(va_list)&arg+sizeof(arg))#define va_arg(ap,t) (*(t*)((ap+=sizeof(t))-sizeof(t)))#define va_end(ap) (ap=(va_list)0)
复制代码

注意:

va_start 中获取到 arg 地址后还要再加 sizeof(arg)才是第一个不确定参数的位置;

va_arg 中先将 ap+=sizeof(t),然后再减去 sizeof(t)得到当前的参数,但不影响 ap 的递增。

例子如下:

void PrintFloats (int n, ...){  int i;  double val;  printf ("Printing floats:");  va_list vl;  va_start(vl,n);  for (i=0;i<n;i++)  {    val=va_arg(vl,double);    printf (" [%.2f]",val);  }  va_end(vl);  printf ("\n");}
int main (){ PrintFloats (3,3.14159,2.71828,1.41421); return 0;}//测试结果:Printing floats: [3.14] [2.72] [1.41]
复制代码


发布于: 刚刚阅读数: 3
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020.02.29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
C++学习---变长参数(stdarg.h)的实现原理_c++_桑榆_InfoQ写作社区