linux va_start 编译,深度探索va_start、va_arg、va_end

采用C语言编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数。但在某些情况下希望函数的参数个数可以根据需要确定。典型的例子有大家熟悉的函数printf()、scanf()和系统调用execl()等。那么它们是怎样实现的呢?

C编译器通常提供了一系列处理这种情况的宏,以屏蔽不同的硬件平台造成的差异,增加程序的可移植性。这些宏包括va_start、va_arg和va_end等。在讲解以上宏之前我们先了解一下调用函数时传入参数的处理过程。

一、函数传入参数过程

一个函数包括函数名、传入参数、返回参数以及函数体,函数体编译后的二进制代码储存在程序代码区。当用户调用某个函数时,系统会通过函数名(C++中会涉及到mangled命名处理)查找函数体入口指针并压入栈中,之后将传入参数以从右至左的顺序压入栈中(栈空间是往低地址方向增长的,也即栈底对应高地址,栈顶对应低地址),示例如下:

#include

using namespacestd;void fun(inta, ...)

{int *temp = &a;

temp++;for (int i = 0; i < a; ++i) { cout<< *temp <

temp++;

}

}intmain()

{int a = 1;int b = 2;int c = 3;int d = 4;

fun(4, a, b, c, d);

system("pause");return 0;

}//Output://1//2//3//4

二、va_start、va_arg和va_end宏定义

接着我们再来看看va_start、va_arg和va_end等宏的具体定义。

#define _INTSIZEOF(n)  ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) //此句宏的作用是将类型n的大小向上取成4的倍数,如n为char型的话结果即为4#ifdef  __cplusplus#define _ADDRESSOF(v)  ( &reinterpret_cast(v) ) //vs2015中此句高亮,作用是将v的地址重新解释成char*型

#else

#define _ADDRESSOF(v)  ( &(v) )

#endif

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )

#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define _crt_va_end(ap)      ( ap = (va_list)0 )#ifndef _VA_LIST_DEFINED

#ifdef _M_CEE_PURE

typedef System::ArgIterator va_list;#elsetypedefchar *  va_list;      //vs2015中此句高亮

#endif /* _M_CEE_PURE */

#define _VA_LIST_DEFINED

#endif

//以上宏定义出现在vadef.h,通过stdio.h即可使用

#define va_start _crt_va_start

#define va_arg _crt_va_arg

#define va_end _crt_va_end

//以上宏定义出现在stdarg.h中,若要使用则需加上 #include

三、va_start、va_arg和va_end使用示例

容易看出,以上宏定义主要涉及地址操作,va_start获取第二个传入参数的地址给va_list类型变量(假设为arg_ptr),va_arg用于获取当前参数值并将指针arg_ptr往后移,va_end则是将arg_ptr置为空。va_start、va_arg、va_end具体用法示例如下:

#include #include

int fun(int x, inty) {return x -y;

}int fun(intcount, ...) {

va_list arg_ptr;//等同于 char *arg_ptr;

int nArgValue =count;int nArgCout = 0;

va_start(arg_ptr, count);//使arg_ptr指向第二个参数的地址

printf("The 1 th arg: %d\n", nArgValue);    //输出第一个参数的值

int sum = 0;for (int i = 0; i < count; i++)

{++nArgCout;

nArgValue= va_arg(arg_ptr, int);//将arg_ptr所指参数返回成int并移动arg_ptr使其指向后一个参数,这里假设传入参数均是int型

printf("The %d th arg: %d\n", i+2, nArgValue);  //输出各参数的值

sum +=nArgValue;

}

va_end(arg_ptr);//将arg_ptr置为空

returnsum;

}intmain() {

cout<< fun(0) <

cout<< fun(1, 1) << endl;  //优先匹配到函数int fun(int, int), 输出0

cout << fun(2, 3, 4) <

//Output://0//0//The 1 th arg: 2//The 2 th arg: 3//The 3 th arg: 4//7

注意:以上宏操作并不提供参数个数获取操作,这需要用户在函数中获取,如第二个fun函数使用count指明个数,printf通过解析第一个传入参数来确定参数个数与类型等。

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值