c++ 的魔法,va_arg 不能在 64 位架构上正常工作

magic with c++, va_arg doesn't work on 64 bit architecture with double properly

我有一个非常有趣的问题,我的朋友和同事中没有人不能帮助我。 那么,让我们看看下一个c++代码:

#include <stdio.h>
#include <stdarg.h>
typedef void* VirtualMethodTable;
void funcM(void* __this, ...);
__interface Ix
{
    void __cdecl qq(long a, long b, double x, long c);
};
struct tagInterface
{
    tagInterface()
    {
        VirtualMethodTable* VMT = new VirtualMethodTable[1];
        VMT[0] = (void*)&funcM; //here's funcM assignment
        this->VMT = VMT;
    }
    ~tagInterface(){ delete[] VMT; }
    VirtualMethodTable* VMT;
};
void func1(long a, long b, double x, long c)
{
    //some_logic
}
void funcM(void* __this, ...)
{
    va_list marker;
    va_start(marker, __this);
    marker -= sizeof(__this); // line 1
    tagInterface* inst = va_arg(marker, tagInterface*); //line 2
    //we can comment line 1 and line 2 and it still will work as earlier (doesn't work)
    long l1 = va_arg(marker, long);
    long l2 = va_arg(marker, long);
    double d = va_arg(marker, double);//d = 4.343564450161e-311#DEN, not 3.3
    long l4 = va_arg(marker, long);
    func1(l1, l2, d, l4);
    va_end(marker);
}
long main()
{   
    tagInterface x;
    Ix* ins = (Ix*)((void*)&x);
    long p1 = 1;
    long p2 = 2;
    double p3 = 3.3;
    long p4 = 4;
    ins->qq(p1, p2, p3, p4); //it will call funcM 
}

在 Win32 架构上运行良好(visual studio 2013,win7x64)
但是当我在 x64 上启动它时,函数 funcM 中的 "d" 变量具有“4.343564450161e-311#DEN”值
"l1"、"l2"、"l4"、"inst" 等其他变量正常启动。
虽然,我已经尝试使用 'float',但它也不起作用!
我搜索了所有 va_arg 个堆栈溢出问题,但没有找到答案!
那我哪里错了?
谢谢!
更新 1.:
是的,它不起作用,因为 "conceptual stack doesn't match the physical stack"
"d" 变量通过 xmm3 寄存器并 va_arg 尝试与 xmm0 一起工作。
希望 smbd 有时会发现它很有用!
更新 2. 问题的解决!
对于 64 位程序,调用约定不同:值并不总是通过堆栈传递:https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx。其中一些通过寄存器传递。因此,这里不能使用va_list(与堆栈一起使用,适用于可变参数函数)。
试试这个解决方法:

struct ClassIx
{
       virtual void __cdecl qq(...);
};
ClassIx* ins = (ClassIx*)((void*)&x);
ins->qq(p1, p2, p3, p4); //it will call funcM


来自 MSDN 的 Viorel_:
https://social.msdn.microsoft.com/Forums/en-US/7a2d9bb5-2b83-4bc5-a018-d7b460fa5550/magic-with-c-vaarg-doesnt-work-on-64-bit-architecture-with-double-properly?forum=vcgeneral

va_arg 函数必须这样声明和调用。您不调用 va_arg 函数,而是调用 void __cdecl Ix::qq(long a, long b, double x, long c);.

部分问题可能是 "I just push parametres on the stack"。 堆栈 不存在。有一个概念性的 C++ 调用堆栈(所有已调用但尚未调用的函数的列表 return,按时间顺序,及其参数)和一个物理 x64 堆栈(RBP/RSP 寄存器)。

您的问题是概念堆栈与物理堆栈不匹配。并非所有函数参数都在物理堆栈上。但是,var_arg 机制必须动态找出函数参数的位置,这可能意味着可变函数的参数 在物理堆栈上。