2013-07-18 25 views
11

典型的例子:在易發生異常的代碼中使用va_list是否安全?

void foo(const char *fmt, ...) 
{ 
    va_list args; 
    va_start(args, fmt); 

    // might throw, might not. who knows. 
    bar(fmt, args); 

    // uh-oh... 
    va_end(args); 
} 

這是一個糟糕的主意,即是它罕見的使用在C++ va_list?如果我將bar換成try-catch,是否有幫助?什麼是一些替代品?

+0

恕我直言變長參數列表是一個壞主意。看看'iostream'模型的合理解決方案。 –

+0

只需將它包裹在try/catch中,它應該是「安全的」。 – jmucchiello

+0

@EdHeal可變長度參數列表很好,只要您使用可變參數模板實現它們即可。 –

回答

4

C++標準遵循va_start等規範的C標準。 C標準有這樣的說法:

7.15.1p1 ... va_start和va_copy宏的每個調用都應該在同一個函數中通過相應的va_end宏調用進行匹配。

因此,如果您在致電va_start之後但在va_end之前以任何方式退出該功能,那麼您的程序會顯示未定義的行爲。

是的,包裝bartry/catch將有所幫助。

+0

這當然是100%真實的,我不主張編寫假設具體實現的代碼,但我只是想拋出那些我已經看到的'va_end'的三個實現都是'ap =(va_list)0;',即空列表。所以不會調用'va_end'會在技術上導致UB,但實際上可能不會。同樣,不主張在這裏寫錯誤的代碼,而且樣本量不大。該標準必須具體說明如何使用'va_list'及其相關函數,但實際上,這可能並不重要。 –

3

C++標準將此推遲到C標準。

C99(草案)7.15.1/1告訴我們:

所述的va_start的每次調用和va_copy宏應由 的va_end用來宏中相同功能的相應調用相匹配。

因此,如果bar拋出,你不能執行va_end和你的程序未定義的行爲。如果你添加一個try/catch來確保va_end總是按需要調用,那麼你應該沒問題。但是請記住,您不能將非POD作爲可變參數傳遞,所以如果您需要處理它們,則無論如何您都需要一個備用機制。

更多的C++類似的替代方案可能是插入運算符(operator<<),如在語言提供的各種iostream中所見。

+9

...或C++ 11可變參數模板。然後我們不再需要可怕的iostream鏈接的操作符黑客。 –

0

如上所述,它是c標準未定義的行爲。 然而,根據你的平臺,marco可以編譯成不同的東西,例如我看到它只是args = 0;而且va_list是一個char *;在這種情況下,最終的宏觀似乎沒有做任何關鍵的事情。除了我不確定誰會釋放參數,我不知道他們是在哪裏分配的。

我不以任何方式推薦使用此功能,但有時爲了支持遺留代碼,有時需要瘋狂的東西。如果你可以使用try catch,那麼一定要這樣做。