使用变量列表参数时 va_list 的长度?

新手上路,请多包涵

有没有办法计算 va_list 的长度?我看到的所有示例都明确给出了变量参数的数量。

原文由 Anton Kazennikov 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

无法计算 va_list 的长度,这就是为什么您需要 printf 中的格式字符串之类的函数。

可用于处理 va_list 的唯一功能宏是:

  • va_start 开始使用 va_list
  • va_arg - 获取下一个参数
  • va_end - 停止使用 va_list
  • va_copy (C++11 和 C99 起)- 复制 va_list

请注意,您需要在同一范围内调用 va_startva_end 这意味着您不能将其包装在一个实用程序类中,该实用程序类在其构造函数中调用 va_startva_end 在它的析构函数中(我曾经被这个咬过)。

例如,这个类毫无价值:

 class arg_list {
    va_list vl;
public:
    arg_list(const int& n) { va_start(vl, n); }
    ~arg_list() { va_end(vl); }
    int arg() {
        return static_cast<int>(va_arg(vl, int));
    }
};

GCC 输出 以下错误

t.cpp:在构造函数 arg_list::arg_list(const int&)

第 7 行:错误: va_start 用于具有固定参数的函数

编译因 -Wfatal-errors 而终止。

原文由 Motti 发布,翻译遵循 CC BY-SA 4.0 许可协议

嗯,如果你不害怕讨厌的 asm hack,那么你可以利用编译器的调用约定。但是,这会将您的代码限制为特定的平台/编译器/调用约定。

例如,在 BDS2006 C++ 32bit x86 Windows 应用程序(我将仅参考此平台)中,将参数放入堆栈然后调用,然后在函数返回后修复堆栈指针值(通过使用堆栈的大小)。这里的小例子:

 double x;
x=min(10.0,20.0,30.0,40.0,50.0);

电话被翻译成这样:

 Unit1.cpp.28: x=min(10.0,20.0,30.0,40.0,50.0);
00401B9C 6800004940       push $40490000
00401BA1 6A00             push $00
00401BA3 6800004440       push $40440000
00401BA8 6A00             push $00
00401BAA 6800003E40       push $403e0000
00401BAF 6A00             push $00
00401BB1 6800003440       push $40340000
00401BB6 6A00             push $00
00401BB8 6800002440       push $40240000
00401BBD 6A00             push $00
00401BBF E894FDFFFF       call min(double,double,????)
00401BC4 83C428           add esp,$28

注意通话后的最后一条指令。 $28 是 4 个参数和一个返回值消耗的大小。因此,如果您可以在函数中读取该值,则可以准确确定参数的数量(如果它们的大小已知)。所以这里的工作示例:

 double min(double x,double ...) // = min(x,y)
        {
        int n,dn=sizeof(double);
        asm {
            mov eax,esp // store original stack pointer
            mov esp,ebp // get to the parrent scope stack pointer
            pop ebx
            pop ebx     // this reads the return address of the call pointing to the first instruction after it which is what we want
            mov esp,eax // restore stack pointer
            sub eax,eax; // just eax=0
            mov al,[ebx+2] // read lowest BYTE of eax with the $28 from the add esp,$28
            mov n,eax // store result to local variable for usage
            }
        n-=dn;  // remove return value  from the count

        double z; z=x;
        va_list va;
        va_start(va,x); n-=dn;
        for (;n>=0;n-=dn)
            {
            x=va_arg(va,double);
            if (z>x) z=x;
            }
        va_end(va);
        return z;
        }

注意每个编译器可以有不同的调用顺序,所以在使用前调试时首先检查汇编列表!!!

原文由 Spektre 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题