2013-11-25 17 views
4

我有一個關於可變的參數個數幾個問題:可變數字

  1. 爲什麼va_startva_arg,並且va_end定義爲宏而不是函數?

  2. 如何va_start工作?它是否有權訪問函數調用堆棧並遍歷堆棧直到找到最後指定的參數?

+0

我想你應該離開c varaidic論證列表並切換到C++ 11 [variadic templates](http://thenewcpp.wordpress.com/2011/11/23/variadic-templates-part-1-2/ ) – smac89

回答

2

爲什麼他們是宏覆蓋在Rationale for International Standard—Programming Languages—C在第7.15可變參數的理由它說:

的va_start並在va_arg必須存在的宏,因爲的va_start使用的參數爲 路過名稱和va_arg使用一個參數,它是數據類型的名稱。

本文How Variable Argument Lists Work in C介紹爲什麼在更詳細,並給出了可能的實現:

typedef char *va_list; 
#define va_start(list, param) (list = (va_list)(&param + sizeof(param))) 
#define va_arg(list, type) (*(type *)((list += sizeof(type)) - sizeof(type)) 

C++你有很多其他選擇,並Variable number of arguments in C++?可能涵蓋所有的人。

1

va_end不需要被實現爲宏,都不是,我認爲,確實的va_start(你只需要添加 &到參數指針傳遞給他們)。 其實va_endva_start必須imlemented如宏,因爲你不能在所有情況下使用&,如在評論中指出。

va_arg必須作爲一個宏,因爲你需要提供一個類型作爲參數傳遞給它,你不能沒有做宏來實現。

va_start按照你的設想工作:你給它第一個參數,它可以根據該參數的大小計算其他參數的位置,因爲它們在堆棧上都是連續的。

它只是啓動va_list指向第一個參數(您傳遞給va_start)的末尾,並在每次使用va_arg時增加下一個的大小。

+0

如果參數是用寄存器存儲類說明符聲明的話,'&'技巧將不起作用。 (我不得不重新檢查標準以查看它是否禁止聲明前置參數register)。當參數以複雜的方式對齊或實際上通過寄存器傳遞時,它也會變得棘手,而gcc只是使用魔法內置功能來使所有的工作都能正常工作。 (在SPARC和MIPS上實現stdarg宏很困難,而且很有指導性:-)) – torek

+0

@torek謝謝,我不記得'register'。我糾正了我的答案。 – Jay

0

va_startva_argva_end通常必須在可變參數函數來完成他們的工作的上下文中執行,因此使得它們的功能會令他們無論是複雜得多,或者(在某些情況下)完全不可能實現的。

它是如何工作的細節留給實施。在典型的情況下,參數可變參數的功能將被推左到右,所以第一個參數將是最接近於堆疊的頂部。爲了得到它,va_arg只需要知道一個堆棧幀,基本結構如寄信人地址是多大(頂部的說法通常是正確的旁邊)。

+0

'va_start'和'va_end'確實需要在可變參數中使用,是的;但'va_arg'可用於從可變參數函數調用的函數中。由於其他人指出的原因,它通常仍然是一個宏(和/或編譯器內置)。 – torek