如果写过JS的话,就知道在JS中定义一个函数,就算输入的实参和形参不一致,也可以同过arguments获取参数
function abc(x)
{
console.log(x)
console.log(arguments[0])
console.log(arguments[1])
console.log(arguments[2])
}
abc(1,2,3)
上例输出
1
1
2
3
在C/C++中其实也可以通过一些方式,获取实参中没有定义的多余参数,被称作变参函数
以下代码在32位 vs2015 win10中调试通过
变参函数
在C/C++定义一个变参函数需要使用三个点…,定义形参
void abc(...)
然后实参的话,就可以随便传入任何类型和数量的参数
abc(3,4,"aaa");
要在被调用的函数中取得参数值的话,在C/C++中有专们的函数来处理接收实参,一般情况下需要三个宏和一个类型来配合使用
基本使用如下
void abc(int i,...)
{
va_list argptr;
va_start(argptr, i);
int aaa = va_arg(argptr, int);
char * bbb = va_arg(argptr, char*);
va_end(argptr);
printf("i:%d,aaa:%d,bbb:%s",i,aaa,bbb);
}
int main() {
abc(3,4,"aaa");
return 1;
}
以上代码输出
i:3,aaa:4,bbb:aaa
宏定义说明如下:
va_list:定义了使用va_start,va_arg,va_end所需要的一些信息
va_start:表示的是设置头一个参数,它的第一个参数是va_list 当以的变量,第二个参数是函数形参的变量名
va_arg:表示从第二个参数开始获取实参,它的第一个参数是va_list定义的变量,第二个参数是函数第二个参数的类型名称,每调用一次va_arg,就获取下一个参数
va_end:表示清理va_list定义
使用时的整个流程如下
1.定义va_list
2.将第一步定义的va_list,传入va_start,获取函数参数的第一个
3.反复调用va_arg,获取变参函数未定义在形参内的变量
4.用va_end清理va_list
整个使用方式流程并不复杂
变参函数的实现原理
实际上实现上述操作,并不复杂,只要知道函数是如何调用的,参数是如何传入的,如何被存储的就可以。
调用一个函数,如下例
void abc(int a,int b)
{
printf("%d,%d", a, b);
}
int main() {
abc(1, 2);
return 1;
}
执行这个函数时,首页要从最后一个参数2,开始把参数都压入栈内,然后执行call调用abc
push 2
push 1
call abc
add esp,8
在执行到abc内以后,存储在栈内参数如下
可以看到参数都是排列在一起的,所以只要知道第一个参数地址,后面的值,按照他所占用空间大小就可以拿出来
自己实现va_start,va_arg,va_end
按上述描述,事实上参数都是挨着的,只要知道第一个参数的地址,后面就都能知道,所以可以模仿也写一个
代码如下
#define my_va_start(a,b) a =(char*)&b
#define my_va_arg(a,b) *((b*)(a = a + sizeof(b)))
#define my_va_end(a) a = 0
void abc(int i, ...)
{
char* my_wa_list;
my_va_start(my_wa_list, i);
int aaa = my_va_arg(my_wa_list, int);
char * bbb = my_va_arg(my_wa_list, char*);
my_va_end(my_wa_list);
printf("i:%d,aaa:%d,bbb:%s", i, aaa, bbb);
}
int main() {
abc(3,4,"aaa");
return 1;
}
以上代码输出
i:3,aaa:4,bbb:aaa
用更简单的方法实现
在一个函数被call的时候,实际上,参数存在的位置前两已经存在两个值
第一个框:记录了在调用函数时,调用函数EBP的内容
第二个框:记录了被调用函数执行完毕后,要返回的代码地址,也就是执行完call abc后要执行的下一句地址
然后,后面就是参数了
所以取值时候,只需要取到EBP+8以后的两个值,就是参数的两个值,所以,函数即使没有形参,也可以取值
知道取值方式,实际上都不需要函数的第一个参数的地址,就可以取出,下面用内嵌asm实现
void abc(...)
{
int a, b;
char* c;
_asm {
mov eax,[ebp+8]
mov a,eax
mov eax,[ebp+12]
mov b,eax
mov eax,[ebp+16]
mov c,eax
}
printf("a:%d,b:%d,c:%s", a, b, c);
}
int main() {
abc(3, 4, "aaa");
return 1;
}
以上代码输出
a:3,b:4,c:aaa