[英]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_start
, va_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.