2017-04-03 241 views
2

使用myPrint()函數的很長的字符串會崩潰。vsnprintf返回給定緩衝區大小的大小

我認爲vsnprintf()無法從linux手冊頁返回緩衝區長度的寫入大小。

我期望的字符串是緩衝區大小的截斷字符串,但從下面的測試代碼是完全錯誤的。

下面有什麼錯?

void myPrint(const char* fmt, ...) 
{ 
    char buffer[512] = {0,}; 

    va_list arg; 
    va_start(arg, fmt); 

    int r = vsnprintf(buffer, 511, fmt, arg); // buffer size is given 
    if (r > 0)    // works correctly 
     buffer[r+1] = '\0'; // crash because r is 200,000 
    va_end(arg); 
} 

int main(int, char**) 
{ 
    const char * data = "abcdefg...." // assuming that a length is 200,000 byte string 
    myPrint("%s\n", data); 
} 
+0

你不需要調用,'vsnprintf'已經做那之後終止字符串。 – user694733

回答

6

不,vsnprintf非常具體地返回完整字符串所需的字符數。​​:

的vsnprintf函數返回將已被寫入字符的數目已被Ñ足夠大,不計算終止空字符,或負的值,如果發生的編碼誤差。因此,當且僅當返回值是非負的且小於n時,空終止的輸出才被完全寫入。

此外,輸入大小應該是整個緩衝區大小,例如,這裏是512.然後vsnprintf將寫入最多511個字符,並在寫入最後一個字符後添加終止'\0'(C11 snprintf description)

否則,超出n-1的輸出字符將被丟棄而不是寫入數組,並且在實際寫入數組的字符末尾寫入空字符。如果在重疊對象之間進行復制,則行爲不確定。

此外,注意(7.21.6.5p2):

[...]因此,空值終止輸出已被完全寫入,當且僅當返回的值是負,並且小於n。

也就是說,如果你的緩衝區是512 char數組,你在512通過,該字符串是否被正確寫入,而不是被截斷IFF的*snprintf返回n0 <= n <= 511


當心Microsoft Visual C++有一個非常破碎的功能,名稱爲_vsnprintf即:

[...]返回字符數wr如果要寫入的字符數小於或等於count, 如果要寫入的字符數大於count,則這些函數返回-1,指示輸出已被截斷。


最後,如果你正在寫僅適用於Linux/Glibc的特定的代碼,你也可以考慮使用vasprintf,將動態分配大到足以容納整個字符串的緩衝區。

+0

感謝您的詳細解釋! – jay

+0

請注意,'* snprintf()'函數系列還有許多其他舊版本的實現,它們在輸出截斷時也返回了'-1'。這是在C廣泛標準化之前存在的兩種主要行爲之一。 –

+0

@AndrewHenle是,'snprintf'是C99 –

1

有許多東西在這裏解決:

  1. 你可以給整個緩衝區大小vsnprintf

    int r = vsnprintf(buffer, sizeof buffer, fmt, arg); 
    
  2. 你不需要NUL通話後終止緩衝。 vsnprintf正確地截斷字符串太長。

  3. vsnprintf返回如果截斷沒有發生就會出現的長度。如果您需要檢測截斷,您可以按如下做到這一點:

    if(r >= sizeof buffer) { 
        // Buffer was too small  
    } 
    
+0

謝謝你的回覆! – jay