2013-07-22 35 views
6

我已經在很多地方閱讀過,但不明白。爲什麼說cout比printf()更安全。僅僅因爲它不需要寫%d %c %f或者它有一些更深的含義。如何cout比printf更類型安全()

在此先感謝。

+2

因爲我們顯式傳遞格式字符串假設如果我用'%f'拼寫'%d'然後 - 未定義的行爲。 –

+2

[printf vs cout in C++]可能的副本(http://stackoverflow.com/questions/2872543/printf-vs-cout-in-c) – 0decimal0

+0

由於您提到類型安全,請參閱http://stackoverflow.com/問題/ 4781819/printf-vs-stdcout和http://stackoverflow.com/questions/2017489/should-i-use-printf-in-my-c-code和... – devnull

回答

10

這就是爲什麼:

printf("%s\n", 42); // this will clobber the stream 

這會導致緩衝區溢出 - 編譯器不能一般檢查在printf第一個參數的格式字符串對應類型的後續參數。它可能在上述情況下做到這一點 - 因爲字符串是硬編碼的 - 有些編譯器可以。 但是通常格式字符串可以在運行時確定,因此編譯器無法檢查其正確性。


但這些檢查屬於特例,以printf。如果您使用與printf相同的簽名編寫您自己的myprintf函數,將會有無法來檢查類型安全性,因爲簽名使用省略號...,它省略函數中的所有類型信息。

+3

GCC的'format'屬性可用於將相同的檢查應用於您自己的'myprintf'函數 –

+0

您的示例不會導致緩衝區溢出!它只會打印字符串地址。 –

+0

@LS_dev對,很好的觀察。我改變了它。 –

1

C++ FAQ

[15.1]我爲什麼要使用<iostream>代替傳統<cstdio>

[...]

更多類型安全:隨着,對象被I/O的類型是由編譯器靜態地已知的。相反,使用「%」字段動態地找出類型。

[...]

printf,編譯器不能檢查的第一個參數的格式腳本對應類型的其他參數...... 一般而言,在運行時完成。

6

printf的家庭功能是可變參數函數,因爲他們都使用省略號...這意味着任何類型的參數(一個或多個),只要...而言可以被傳遞給該函數。有沒有限制由編譯器,因爲有沒有要求類型的論點。編譯器不能施加任何類型安全規則,因爲省略號...允許所有類型。該函數使用格式字符串到假設參數類型(即使有不匹配!!)。 格式字符串在運行時被讀取和解釋,此時編譯器不能做任何事情,如果有不匹配,因爲代碼已經編譯。所以這樣,這不是類型安全的。通過類型安全,我們通常意味着編譯器可以通過對(未評估的)表達式的類型強加規則來檢查程序的類型一致性。

請注意,如果存在不匹配(該函數無法計算出!),程序將進入未定義行爲區域,該程序的行爲不可預測,理論上可能發生任何事情。

您可以將相同的邏輯擴展到任何可變功能函數,如scanf系列。

3
  1. 類型系統保證正確性與std::ostream但不printf。 康拉德的回答就是一個例子,但類似

    printf("%ld\n", 7); 
    

    也斷了(假設longint是您的系統上不同大小)。它甚至可能與一個構建目標一起工作而失敗。試圖打印像size_t這樣的typedefs有同樣的問題。

    這(有點)解決與某些編譯器所提供的診斷,但是,這並不與第二感幫助:在格式字符串中使用

  2. 兩個類型系統(運行時類型系統,以及代碼中使用的編譯時系統)不能自動保持同步。例如,printf與模板嚴重交互:

    template <typename T> void print(T t) { printf("%d\n",t); } 
    

    你不能讓這個正確的爲各類T - 你能做的最好的是static_cast<int>(t)那麼它將無法編譯如果T是不可轉換爲int。比較

    template <typename T> void print(std::ostream& os, T t) { os << t << '\n'; } 
    

    ,其選擇的operator<<爲具有一個任何T正確過載。

2

通常,編譯器無法檢查來自printf的參數,甚至沒有參數計數,也不檢查它們是否適合格式字符串。他們是「優化」來完成這項工作,但是是一種特殊情況,可能會失敗。 實施例:

printf("%s %d\n", 1, "two", 3); 

這將編譯(除非優化編譯器檢測故障),並在運行時,printf的將考慮第一個參數(1)的字符串和第二(「二」)一個整數。 printf甚至不會注意到有第三個參數,如果沒有足夠的參數,它們也不會注意到!

使用cout,編譯器必須爲您插入的每個變量選擇特定運算符< <。 實施例:

cout<<1<<"two"<<3<<endl; 

編譯器必須改變這種在調用對應ostream&operator<<(int)ostream&operator<<(const char*)(和也ostream&operator<<(ios&(*)(ios&)))。

cout也會更快,因爲沒有格式字符串的運行時解釋。