c/c++ 自己设计不定参处理方式,代替va_list

函数调用约定

什么是函数调用约定?我们平常的函数是什么调用约定?不同的调用约定又有什么区别?
以VS2019为例新建的项目在不设置调用约定的情况下的调用约定默认是 __cdecl
WindowsAPI 一般是 __stdcall
类的成员函数 一般是 __thiscall
x64程序默认是 __fastcall

不同调用约定的区别

调用约定传参方式栈平衡
__cdecl自右向左依次压入栈中由调用者恢复栈
__stdcall自右向左依次压入栈中由函数本身(被调用者)恢复栈
__fastcallx32/x64分别 ECX/RCX,EDX/RDX,R8,R9传递前2/4个参数,其余参数自右向左依次压入栈中由函数本身(被调用者)恢复栈
__thiscall由ECX/RCX传递this指针 传参方式按照默认

由上可知 只有__cdecl才可以做不定参函数,因为只有__cdecl是由调用者恢复栈,在VS中给函数加上__stdcall为啥还能定义不定参,是不是我在胡扯呢…那是因为使用了不定参后不管你定义的什么调用约定都会以__cdecl约定编译,如图
在这里插入图片描述
在这里插入图片描述

自己设计不定参的处理方式

既然不定参函数都是__cdecl约定,__cdecl的参数都是在栈上的
而且是从右到左依次压栈,那么我们只要知道栈顶的地址(也就是第一个参数的地址)
就可以获取到其它参数

1.知道第一个参数名

//求平均数函数
intptr_t ave(intptr_t count, ...)
{
	intptr_t* arg = &count;	//这里获取到第一个参数的地址(栈顶)
	//arg[0] 就是第一个参数 arg[1] 就是第二个参数
	intptr_t sum{};
	for (size_t i = 1; i <= arg[0]; i++)
	{
		sum += arg[i];
	}
	return sum / arg[0];
}
int main()
{
	std::cout << ave(3, 1, 2, 3);
	//执行结果 2
}

2.不知道第一个参数名

我们知道局部变量其实也是栈上的空间…他喵的这编译出来的局部变量没有按照哪个先声明,哪个先压栈,而是函数头直接一起压栈,然后ebp - 4,ebp - 8来操作…而且也没有按照声明顺序…可能是编译器飘了吧…
那就只能用汇编把第一个参数地址拿出来了…

intptr_t ave2(...)
{
	intptr_t* arg;
	__asm mov arg,ebp
	intptr_t sum{};
	arg += 2;//arg[0] = 函数头的push ebp ,arg[1] 是 call ave2压入的反回地址 arg[2]才是第一个参数参数
	for (size_t i = 1; i <= arg[0]; i++)
	{
		sum += arg[i];
	}
	return sum / arg[0];
}
int main()
{
	std::cout << ave2(7, 1, 2, 3, 4, 5, 6, 7) << std::endl;
	//输出结果4
}

结束

第一次写文章…不好勿喷…如果有错的地方请指出来…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值