前言
在吸进的一个项目中为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数。在需要的地方调用自定义的MemNew函数。这样就带来一个问题,使用stl的类都有默认构造函数,以及复制构造函数等。但使用共享内存和内存池的类可能没有默认构造函数,而是定义了多个参数的构造函数,于是如何将参数传入MemNew函数便成了问题。
一、变长参数函数
首先回顾一下较多使用的变长参数函数,最经典的便是printf。
extern int printf(const char *format, ...);
以上是一个变长参数的函数声明。我们自己定义一个测试函数:
#include <stdarg.h>
#include <stdio.h>
int testparams(int count, ...)
{
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i)
{
int arg = va_arg(args, int);
printf("arg %d = %d", i, arg);
}
va_end(args);
return 0;
}
int main()
{
testparams(3, 10, 11, 12);
return 0;
}
变长参数函数的解析,使用到三个宏va_start,va_arg 和va_end,再看va_list的定义 typedef char* va_list; 只是一个char指针。
这几个宏如何解析传入的参数呢?
函数的调用,是一个压栈,保存,跳转的过程。
简单的流程描述如下:
1、把参数从右到左依次压入栈;
2、调用call指令,把下一条要执行的指令的地址作为返回地址入栈;(被调用函数执行完后会回到该地址继续执行)
3、当前的ebp(基址指针)入栈保存,然后把当前esp(栈顶指针)赋给ebp作为新函数栈帧的基址;
4、执行被调用函数,局部变量等入栈;
5、返回值放入eax,leave,ebp赋给esp,esp所存的地址赋给ebp;(这里可能需要拷贝临时返回对象)
从返回地址开始继续执行;(把返回地址所存的地址给eip)
由于开始的时候从右至左把参数压栈,va_start 传入最左侧的参数,往右的参数依次更早被压入栈,因此地址依次递增(栈顶地址最小)。va_arg传入当前需要获得的参数的类型,便可以利用 sizeof 计算偏移量,依次获取后面的参数值。
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define _ADDRESSOF(v) (&const_cast<char&>(reinterpret_cast<const volatile char&>(v)))
#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define __crt_va_end(ap) ((void)(ap = (va_list)0))
#define __crt_va_start(ap, x) ((void)(__vcrt_va_start_verify_argument_type<decltype(x)>(), __crt_va_start_a(ap, x)))
#define va_start __crt_va_start
#define va_arg __crt_va_arg
#define va_end __crt_va_end










