C/C++;可变参数;va_list;va_start;va_end;va_arg;
pc : 网上相关资料有很多,但是还是想自己的捋一遍
问题分析
如何才能实现可变参数的功能?
1.如何让函数知道参数个数以及参数的数据类型?
2.使用时如何定位这些参数?
3.最好使用时够简洁和方便
在C/C++下如何实现
解决第一个问题,朴实无华的方法就是 用参数样式模板字符串 fmt(format)来指出参数的类型和数量
解决第二个问题前,补充了一下基础内容
基础内容
1.va_list的宏定义
百度词条查说的很清楚(直接引用):
#ifdef _M_ALPHA
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;
#else
typedef char * va_list;
#endif
_M_ALPHA是指DEC ALPHA(Alpha AXP)架构。所以一般情况下va_list所定义变量为字符指针。
结论就是:一般情况下va_list就是一个字符指针,除非有定义的_M_ALPHA宏(这并不常见)
2.INTSIZEOF 宏(功能型)
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
此宏的作用就是为了求出(栈中参数 数据对齐, 好定位fmt上面的各个参数) :
sizeof(int)(sizeof(int)-1+1)的倍数中sizeof(n)的最小上界(具体可百度:(a+b)&~b)
3.VA_START宏
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
就是通过const cahr *fmt偏移定位可变参数
4.VA_ARG宏
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
和VA_START宏功能一样*fmt偏移定位下一个可变参数
带来一个问题:这个’t’哪里来,fmt来…emm怎么提取出来的?
5.VA_END宏
#define va_end(ap) ( ap = (va_list)0 )
简单明了的宏
资料查找查找我突然明了了,我觉得这几宏做了啥都很明显了
最后还是贴个列子吧:
因为vprint,printf等函数隐藏了对VA_START的使用(所以放弃);
但是我在lua程序设计书里看到了一个有趣的使用方法
(请关注重点,lua程序细节无视就好):
//调用样式:
double x,y,z;
call_val(L,"oneFun","dd>d",x,y,&z)
//
void call_va(lua_State* L, const char * fun, const char *sig, ...)
{
va_list vl;
int narg, nres;
//这里sig就是上边提到的 'fmt'
va_start(vl, sig);
//先将函数入栈
lua_getglobal(L, fun);
//压入参数 并计数
for (narg = 0; *sig; narg++)
{
luaL_checkstack(L, 1, "too many argnment");
switch (*sig++) {
case 'd' :
lua_pushnumber(L, va_arg(vl, double));
break;
case 'i':
lua_pushinteger(L, va_arg(vl, int));
break;
case 's':
lua_pushstring(L, va_arg(vl, char*));
break;
case '>':
goto endarge;
default:
error(L, "invalid opint(%c)", *(sig - 1));
}
}
endarge:
nres = strlen(sig);
//运行函数
if (lua_pcall(L, narg, nres, 0) != LUA_OK)
error(L, "error calling '%s' : '%s' ", fun, lua_tostring(L, -1));
//获取结果
nres = -nres;
while (*sig)
{
switch (*sig++)
{
case 'd':
{
int isnum;
double n = lua_tonumberx(L, nres, &isnum);
if (!isnum)
error(L, "wrong result type");
*va_arg(vl, double*) = n;
break;
}
case 'i':
{
int isnum;
int n = lua_tointegerx(L, nres, &isnum);
if (!isnum)
error(L, "wrong result type");
*va_arg(vl, int*) = n;
break;
}
case 's':
{
const char* s = lua_tostring(L, nres);
if (s == NULL)
error(L, "wrong result type ");
*va_arg(vl, const char**) = s;
break;
}
default:
error(L, "invalid option (%c)", *(sig-1));
}
nres++;
}
va_end(vl);
}