vsnprintf返回给定缓冲区大小的大小

jay*_*jay 2 c c++ linux api

带有myPrint()函数的非常长的字符串将崩溃.

我认为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);
}
Run Code Online (Sandbox Code Playgroud)

Ant*_*ala 6

不,vsnprintf非常具体地返回完整字符串所需的字符数.C11 7.21.6.12p3:

vsnprintf函数返回已经写入的字符数已足够大,不计算终止空字符,如果发生编码错误则返回负值.因此,当且仅当返回值是非负且小于n时,才完全写入以null结尾的输出.

另外,输入大小应该是完整的缓冲区大小,例如512.然后vsnprintf将写入最多511个字符并'\0'在写入最后一个字符后添加终止.(C11 snprintf说明):

否则,丢弃n-1之外的输出字符而不是写入数组,并在实际写入数组的字符末尾写入空字符.如果在重叠的对象之间进行复制,则行为未定义.

另外,请注意(7.21.6.5p2):

[...]因此,当且仅当返回值为非负且小于n时,才会完全写入以null结尾的输出.

也就是说,如果你的缓冲区是一个512的数组char并且你传入了512,那么该字符串被正确写入并且如果返回n值为*snprintfis 则不被截断0 <= n <= 511


请注意,Microsoft Visual C++的名称非常破碎_vsnprintf:

[...]如果要写入的字符数小于或等于count,则返回写入的字符数; 如果要写入的字符数大于count,则这些函数返回-1表示输出已被截断.


最后,如果您只编写Linux/Glibc特定代码,您还可以考虑使用vasprintf它来动态分配足够大的缓冲区来保存整个字符串.