2009-12-29 126 views
18

我已經在過去使用這種類型的會議多次在我的代碼:sprintf()的輸入和輸出字符串可以相同嗎?

strcpy (cTmpA, "hello"); 
sprintf (cTmpA, "%s world", cTmpA); 

最近我打開我的遺留的C編譯器Visual Studio 2005中,發現我從上面的代碼產生的亂碼字符串。然後,我想到sprintf()的行爲可能並非嚴格定義,其中一個輸入與輸出字符串匹配。

上述代碼是否有效K & R C?如果沒有,我怎麼找到我的代碼中發生這種類型的調用sprintf()的所有地方?

+2

重複http://stackoverflow.com/questions/1283354/is-sprintfbuffer-s-buffer-safe – Alon

回答

18

雖然它是有效的K & R C,你可能想知道它是否是有效的POSIX - 見sprintf Specification。我們閱讀:

如果複製發生在因調用sprintf()或snprintf()而重疊的對象之間,則結果是未定義的。

8

sprintf()的大部分實現都不會複製格式字符串,而是在傳遞的字符串中使用指針。如果格式和輸出指向相同的內存,那將導致奇怪的結果。

而且你應該真的使用snprintf()它可以保護你免受緩衝區溢出。

要查找所有呼叫,請將#define sprintf +++放入共同標題查找並重新編譯所有來源。這應該會給你一個錯誤列表以及文件名和行號:)或者使用遞歸search'n'replace你的IDE。

如果要修剪下來這個列表到您使用了兩個參數相同指針的,使用這個宏:

#define sprintf(output,format,...) check_sprintf(__FILE__,__LINE__,output,format,....) 

請注意,並非可變參數所有的編譯器支持宏。然後定義一個新的功能check_sprintf

int check_sprintf (char*filename,int line,char*output,char*format,...) { 
    va_list args; 
    int len; 

    if(output==format) { 
     fprintf(stderr, 
      "Output and format are the same at %s:%d", filename, line); 
      abort(); 
    } 

    va_start (args, format); 
    len = vsprintf (output, format, args); 
    va_end (args); 

    return len; 
} 

[編輯]我剛纔看到你在談論的輸出和第一個參數。您可以重複使用上面的代碼,並調用va_arg()以獲取第一個參數並在比較中使用它。

+0

好主意使用宏替代來發現這種情況發生。我想到了這一點,但想不到一種方法來製造只在輸出也是輸入之一時導致編譯時錯誤的宏。重新定義sprintf()的問題是,這隻會在運行時顯示,而我的項目足夠大,以至於如果沒有某種類型的自動化單元測試,我絕不會找到每條代碼路徑。 – Piers

+0

隨着時間的推移,你會發現所有的地方,因爲程序崩潰困難(而不是靜靜地產生越野車數據)。因此,從現在的單元測試開始,儘可能多地修復,然後在錯誤報告進入時修復其餘部分。 –

相關問題