的swprintf截断导致意外的输出

问题描述 投票:2回答:1

我在修这在Linux和Windows,并在某些情况下,都应该包含格式化的内容缓冲区是比内容更小运行的遗留代码。

该代码使用的swprintf其中根据documentation

大小 - 高达大小 - 1个字可以写,加空终止

确实会截断字符串,但在尝试它coliru我遇到了意想不到的结果:

#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;
}

将导致1111??

#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;
}

工作得很好。

怎么了 ?

附:我希望我能一切更改为C ++流/字符串,但我不能,wchar_t阵列的各个角落。

c++ printf
1个回答
4
投票

TL;博士:对于某种原因,这些空终止语义依赖于函数调用成功,并为swprintf如果缓冲区足够大,它才能成功。因此,在您第一次尝试的阵列是不是空终止。


这是潜移默化的,但swprintf不像snprintf。它不“最多N-1个字符”编写,并认为成功的在所有情况下。

下面是相同的文件说,有关从swprintf返回值:

返回值:如果写宽字符(不包括终止空宽字符)数是否发生编码错误或如果将要产生的字符数等于或大于尺寸(包括当大小为零)成功或负值

而且,事实上,your attempt returns -1

从这个(从注意,报价下),我们能够确定swprintf考虑操作是否有未在所提供的输出缓冲足够的字节失败。它不会溢出该缓冲区,但它也可能无法完成其工作,其工作包括编写一个NULL终止符。如果没有NULL终止符,你是wchar_t* [有效]传递给std::wcout将运行出界,你的程序未定义的行为。


我承认,这,在休闲阅读,似乎矛盾围绕size参数的语义,为此,C11规定:

不超过n宽字符更被写入,包括终止空宽字符,它总是被添加(unlessn是零)。

......没有说明在函数调用是否成功,否则任何条件。

有可能是要求在标准这个社论缺陷,或执行错误的范围。但是,即使没有是真的,你的函数调用被认为是不成功的,而且我不认为你应该相应地依靠胜负。

我们至少可以看到的libc意图以上破败,从this manual page on Formatted Output Functions匹配:

返回值是给定的输入生成的字符的数量,不包括尾部零。如果不是所有的输出适合提供的缓冲区返回负值。你应该有一个更大的输出字符串再试一次。注:这是从的snprintf如何处理这种情况的不同。


你将不得不听从上述说明:

而窄串提供的std ::的snprintf,这使得能够确定所需的输出缓冲器大小,没有等效为宽字符串,并以确定的缓冲器大小,该程序可能需要调用的std ::的swprintf,检查结果值,并重新分配一个更大的缓冲区,再次尝试,直到成功。

......或者完全切换到一些其他的功能。

© www.soinside.com 2019 - 2024. All rights reserved.