C++ swprintf截断导致意外输出

C++ swprintf截断导致意外输出,c++,printf,C++,Printf,我正在修复在linux和windows上运行的遗留代码,在某些情况下,应该包含格式化内容的缓冲区小于该内容 该代码使用swprintf,根据 可以写入大小不超过大小1的字符,以及空终止符 确实会截断字符串,但在尝试时遇到意外结果: #include <iostream> #include <string> #include <cwchar> int main() { wchar_t wide[5]; std::swprintf(wi

我正在修复在linux和windows上运行的遗留代码,在某些情况下,应该包含格式化内容的缓冲区小于该内容

该代码使用swprintf,根据

可以写入大小不超过大小1的字符,以及空终止符

确实会截断字符串,但在尝试时遇到意外结果:

#include <iostream> 
#include <string> 
#include <cwchar> 

int main()
{

    wchar_t wide[5];

    std::swprintf(wide, sizeof wide/sizeof *wide, L"%ls", L"111111111");

    std::wcout << wide;
}
很好用

怎么了

附言。 我希望我能把所有的东西都转换成C++流/字符串,但是我不能,WCARGYT数组到处都是使用< /P> < P> TL;dr:出于这样或那样的原因,这些空终止语义依赖于函数调用的成功,而对于swprintf,只有在缓冲区足够大的情况下才会成功。因此,第一次尝试中的数组不是以null结尾的

这很微妙,但swprintf与snprintf不同。它最多不写N-1个字符,并认为在所有情况下都是成功的。 以下是同一文档中关于swprintf返回值的说明:

返回值:写入的宽字符数,如果成功,则不计算终止的空宽字符;如果发生编码错误,或如果要生成的字符数等于或大于大小(包括大小为零时),则返回负值

事实上

从这一点以及该引用下面的注释中,我们可以确定,如果提供的输出缓冲区中没有足够的字节,swprintf将该操作视为失败。它不会溢出该缓冲区,但也可能无法完成其工作,其工作包括编写空终止符。如果没有空终止符,您正在[有效地]传递给std::wcout的wchar\u t*将超出界限,并且您的程序具有未定义的行为

我承认,随便一读,这似乎与size参数的语义相矛盾,C11指出:

写入的宽度字符不超过n个,包括一个终止的空宽度字符,除非ESSN为零,否则将始终添加该字符

…没有说明函数调用是否成功的任何条件

这可能是标准中的编辑缺陷或实现缺陷。但是,即使两者都不正确,您的函数调用也被视为不成功,我认为您不应该依赖相应的结果

我们至少可以看到,libc的意图与上述情况相匹配,具体如下:

返回值是为给定输入生成的字符数,不包括尾随的null。如果并非所有输出都适合提供的缓冲区,则返回负值。您应该使用更大的输出字符串重试。注意:这与snprintf处理这种情况的方式不同

你必须注意上面提到的注意事项:

虽然窄字符串提供std::snprintf,从而可以确定所需的输出缓冲区大小,但宽字符串没有等效值,为了确定缓冲区大小,程序可能需要调用std::swprintf,检查结果值,并重新分配较大的缓冲区,然后重试,直到成功


…或者完全切换到其他功能。

仅仅因为你使用了大量数组并不意味着你就不能使用流I/O。std::ostream有一种方法,你可以将数组的一部分传递给我。我的意思是,这不像标准库流IO是API设计的典范。@Spinkoo这很好,swprintf采用缓冲区大小进行边界检查和截断。这听起来更像是OP的STL库在其swprintf实现中有一个bug,如果缓冲区被填充到允许的最大字符数,它就不能正确地写出终止null。啊,是的,我同意you@RemyLebeau我不相信这是一个bug,尽管在我看来,它在C语言中有点不够具体。请看我的答案-欢迎您的想法!是的,我明白这是怎么解释的,当你说其他功能时,你的意思是有另一种通过截断复制基于wchar_t*的字符串的便携方法吗?@Darius老实说,我没有考虑可能的替代方法是什么-留给读者练习,对不起@大流士:有人在寻找上述替代方案时,可能会有一些想法;至少,他们和相关公司std.c讨论中的人员都同意存在一个问题。
#include <iostream> 
#include <string> 
#include <cwchar> 

int main()
{

    wchar_t wide[20];

    std::swprintf(wide, sizeof wide/sizeof *wide, L"%ls", L"111111111");

    std::wcout << wide;
}