2013-05-10 183 views
1

我目前正在更改應用程序中一類函數的函數簽名。這些函數被存儲在一個函數表中,所以我期待着更改這個函數表。我剛剛意識到在某些情況下,我們已經使用了新的函數簽名。但是因爲所有東西都放在函數表中,所以它們被轉換爲正確的函數類型,所以不會引發任何警告。調用具有太多參數的C函數

當函數被調用時,會傳遞一些額外的參數,這些參數實際上並不是函數聲明的一部分,但它們位於參數列表的末尾。

我不能確定這是否是由單向函數參數在傳遞C.我想做到像sprintf的可變參數,可以保證,它必須是前面的參數就可以解決正確無論是在結束的情況下的參數列表?

它顯然在多個平臺上工作得很好,但出於好奇,我想知道它是如何工作的以及爲什麼它工作。

+0

這將取決於您的調用約定。對於cdecl可能並不重要,但對於像stdcall這樣的東西,你會遇到問題。 – 2013-05-10 12:52:11

+0

可以顯示錶中的函數指針聲明和您調用的函數的實際聲明是什麼? – ouah 2013-05-10 12:52:30

回答

2

您的功能必須使用cdecl調用約定(http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl)。這將以相反的順序從右向左推送參數,以確保最後一個參數可以輕鬆定位(堆棧頂部)並用於解釋餘數,例如printf格式字符串。調用者也有責任清理堆棧,它比函數本身更簡潔(如約定pascal/stdcall),但確保可以使用可變參數列表,並且暗示尾部參數可以是忽略。

+0

cdecl不是唯一可以工作的,實際上任何調用者清理調用約定都可以在這裏很好地工作。 – 2013-05-10 12:55:23

+1

是的,但由於這是一個'c'問題,最有可能的是cdecl。 – 2013-05-10 12:56:15

+0

您可以在pascal/stdcall約定中擁有可變參數列表。你只是有一個'va_end'的平凡定義。但是,如果你沒有得到正確數量的參數,它可能不起作用。 – 2013-05-10 13:06:13

2

但是因爲所有東西都放在正確的函數類型中,所以放入函數表中時不會引發任何警告。

因此,編譯器無可奈何。 C程序員投得太多了。 > _ <

我不能確定這是否是由單向函數參數在傳遞C.我想做到像sprintf的可變參數,可以保證,它必須是前面的參數可以被正確解析的情況下無論是在參數列表的末尾?

從技術上講,你有未定義的行爲。但是,您的平臺定義了以使用標準C調用約定(請參閱Scott的答案)或直接映射到它們的東西(通常通過將前N個參數映射到某組處理器寄存器)。

這也出現了很多變量參數列表。例如,printf聲明是這樣的:

int printf(const char* format, ...); 

,其定義通常採用stdarg系統來處理額外的參數,它看起來像:

#include <stdarg.h> 

int printf(const char* format, ...) 
{ 
    va_list ap; 
    int result; 
    va_start(ap, format); 
    result = vprintf(format, ap); 
    va_end(ap); 
    return result; 
} 

如果你是標準的平臺上C調用約定,那個宏通常變成什麼都不做。在這種情況下,您可以通過向函數傳遞額外的參數來避開。但在某些平臺上,需要調用va_end()來將堆棧恢復到可預測的狀態(即,在致電va_start之前);在這些情況下,你的函數不會像它發現它的方式離開堆棧(它不會從堆棧中彈出足夠的參數),所以你的調用函數可能會在退出時退出,當它獲取返回的假值地址。