2017-04-03 51 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