簡體   English   中英

變異函數混亂

[英]c variadic functions confusion

我試圖找出va_start()和va_arg()宏背后的含義。 下面的代碼運行良好。

#include <iostream>
#include <cstdarg>

void f(double a, double b, ...)
{
   va_list arg;
   va_start(arg, b);
   double d;
   while((d = va_arg(arg, double)) != 0)
      {
         std::cout << d << '\n';
      }
}

int main(int argc, char *argv[])
{
   f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 0.0);
   return 0;
}

正如我所期望的那樣,它提供了這樣的輸出:3 4 5 6 7 8 9.然后我發現了這些宏的定義(在互聯網上,因為我的標頭stdarg.h很神秘-它像_builtin_va_arg(一樣定義了宏va_arg(v,l))。 v,l),最后一個未在其中定義,stdarg.h不包含任何內容,因此在某些庫中也是如此? 不過,我寫了“ cstdarg”代替:

typedef char* va_list;

#define _INTSIZEOF(n) \
((sizeof(n)+sizeof(int)-1) &~(sizeof(int)-1))

#define va_start(ap,v) \
(ap = (va_list)&v + _INTSIZEOF(v))

#define va_arg(ap,t) \
(*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))

#define va_end(ap) (ap = (va_list)0)

輸出變得更奇怪,例如1 -0.0409377 -0.0409377 4.88084e-270 4.85706e-270 1 2 3 4 5 6 7 8 9.我認為可變參數被放置在最后聲明的參數的旁邊,但是顯然復雜得多情況。 如果有人揭露我錯了地方或那里到底發生了什么,我會感到非常高興。

va_startva_arg和別忘了va_end是特定於編譯器的。 您不能只是將它們從其他地方帶走並期望它們起作用。 在使用它們時,最好遵循手冊頁,並且只有當您是編譯器工程師時,才嘗試了解它們的內部工作原理。

PS:哦,他們的定義通常很神秘,使用一些細微的技巧使其起作用。

P.S2:在回答您定義_builtin_va_arg的問題時,它是編譯器已知的,即所謂的Builtin 您將在編譯器的源代碼中找到它;)

您的手寫宏將在始終傳遞堆棧上所有參數並將較小類型填充到sizeof(int)的計算機上工作。 但是,許多機器(最近的日子?)沒有在堆棧上傳遞參數-而是在寄存器中傳遞參數,並且僅在寄存器中容納太多參數時才使用堆棧。

因此,為了處理va_args,編譯器需要知道ABI,以及在什么情況下將哪些參數放在何處。 通常要做的是讓va_list包含多個數組(足以容納可能包含args的所有寄存器)和多個指針(通常,每種類型的寄存器一個,而堆棧的指針)。va_start轉儲所有參數注冊到數組中並初始化指針,然后va_arg找出給定參數類型將傳入哪種寄存器,並將值拉出適當的位置,因此對於一個具有8個reg的整數/指針args的假設處理器,對於float / double args的8條規則,您可能會有類似以下內容:

typedef struct {
    intptr_t iregs[8], *iptr;
    double   fregs[8], *fptr;
    char     *spptr;
} va_list;

inline void _builtin_va_start(va_list &ap, arg) {
    // dump the registers might be used to pass args into ap->iregs and ap-fregs,
    // setup iptr and fptr to point into iregs and fregs after the arguments that
    // correspond to 'arg' and those before it.  spptr points into the stack space
    // used for arguments after regs run out
}
inline _builtin_va_arg(va_list &ap, type) {
    if (type is integer or pointer) {
        if (ap->iptr == ap->iregs+8) {
            rv = *(type *)ap->spptr;
            ap->spptr += sizeof(type);
        } else {
            rv = *ap->iptr++;
        }
    } else if (type is float or double) {
        if (ap->fptr == ap->fregs+8) {
            rv = *(type *)ap->spptr;
            ap->spptr += sizeof(type);
        } else {
            rv = *ap->fptr++;
        }
    } else {
        // some other type (struct?) deal with it
    }
}

注意,這些_builtin_va函數都不_builtin_va C編寫; 它們需要內置到編譯器中

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM