从Visual Studio的版本开始,vsnprintf大多符合标准

Starting at what version of Visual Studio is vsnprintf mostly standard-conformant?

本文关键字:标准 vsnprintf Studio Visual 版本 开始      更新时间:2023-10-16

根据Microsoft的vsnprintf文档,该函数至少从2003年的Visual Studio开始就是C(++)运行时库的一部分。

int vsnprintf( char *buffer,        // Storage location for output
               size_t count,        // Maximum number of characters to write
               const char *format,  // Format specification
               va_list argptr )     // Pointer to list of other arguments

我要问的是:对于哪个版本的Visual Studio是符合C99标准(ISO/IEC 9899:1999)的捆绑C(++)RTL的x86和x64的vsnprintf实现,假设

  • #define _CRT_SECURE_NO_WARNINGS#include <stdio.h>之前执行,这是现代版本的Visual Studio RTL所必需的;
  • 如果count大于零,则buffer是指向(至少)count可写字符的指针;
  • 格式不NULL,并且符合Microsoft的格式规范语法,适用于特定版本的 RTL;
  • count的值和要生成的字符数都足够小,以适应类型 int ;

我们希望一致性包括(除了标称输入的基本功能之外)这些要求(由标准的snprintf规范暗示,vsnprintf引用):

  1. 在上述假设下不产生未定义的行为(包括调用Microsoft的无效参数处理程序);
  2. 返回buffer==NULLcount==0时要写入的长度(不包括终止空字符),从而允许预飞行确定输出的长度;
  3. buffer!=NULLcount>0时,始终使用终止的 null 字符填充输出字符串,并且返回的结果是非负的,包括由于小count而截断的输出。

请注意以下评论:我愿意承认缺乏restrict限定符,因为大多数符合标准的限定符仍在允许范围内。

<小时 />

文档在(3.)方面留下了模糊的一致性;据我所知,与Visual Studio Community 2015捆绑在一起的实现很好,但并非所有都是。

如果末尾有空间(即,如果要写入的字符数小于 count ),则缓冲区将以 null 结尾。

文档的措辞也明确暗示vsnprintfbuffer==NULLcount==0时不符合C99标准(1.)和(2.);但文档的这些部分似乎被证明是错误的:

如果要写入的字符数大于 count ,则这些函数返回 -1,表示输出已被截断。

如果bufferformat NULL,或者count小于或等于零,则这些函数将调用无效的参数处理程序

<小时 />

测试代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdarg.h>
int f( char *buffer,
       size_t count,
       const char *format,
       ...
     )
{
    va_list vArgs;
    int vRes;
    va_start(vArgs, format);
    vRes = vsnprintf( buffer, count, format, vArgs);
    va_end(vArgs);
    return vRes;
}
int main(void)
{
    char vBuf[6];
    int j, count;
#ifdef _MSC_VER
    printf("_MSC_VER = %ldn",(long)(_MSC_VER));
#else
    printf("_MSC_VER is undefinedn");
#endif
    printf("f(NULL,0,"%%d",777):%3dn", f(NULL,0,"%d",777));
    for(count=0 ;count<=sizeof(vBuf); ++count)
    {
        for(j=0; j<sizeof(vBuf)-1; ++j)
            vBuf[j] = '!';
        vBuf[j] = 0;
        j =  f(vBuf,count,"%d",777);
        printf("f(vBuf,%d,"%%d",777):%3d  vBuf: "%s"n",count,j,vBuf);
    }
    return 0;
}

在我的 Visual Studio Community 2015 安装下给予

_MSC_VER = 1900
f(NULL,0,"%d",777):  3
f(vBuf,0,"%d",777):  3  vBuf: "!!!!!"
f(vBuf,1,"%d",777):  3  vBuf: ""
f(vBuf,2,"%d",777):  3  vBuf: "7"
f(vBuf,3,"%d",777):  3  vBuf: "77"
f(vBuf,4,"%d",777):  3  vBuf: "777"
f(vBuf,5,"%d",777):  3  vBuf: "777"
f(vBuf,6,"%d",777):  3  vBuf: "777"

并在Visual Studio 2008的某些安装下(我相信SP1 + PSDK 7.1)

_MSC_VER = 1500
f(NULL,0,"%d",777):  3
f(vBuf,0,"%d",777): -1  vBuf: "!!!!!"
f(vBuf,1,"%d",777): -1  vBuf: "7!!!!"
f(vBuf,2,"%d",777): -1  vBuf: "77!!!"
f(vBuf,3,"%d",777):  3  vBuf: "777!!"
f(vBuf,4,"%d",777):  3  vBuf: "777"
f(vBuf,5,"%d",777):  3  vBuf: "777"
f(vBuf,6,"%d",777):  3  vBuf: "777"
请注意,即使

输出为正数,count==3也缺少终止空字符。

你现在提到的页面给出了答案:

从Visual Studio 2015和Windows 10中的UCRT开始,vsnprintf不再与_vsnprintf相同。vsnprintf 函数符合 C99 标准;保留_vnsprintf是为了向后兼容较旧的 Visual Studio 代码。

并且您的输出与_vsnprintf一致:

如果要写入的字符数小于或等于 count,则 _vsnprintf 和 _vsnwprintf 函数都返回写入的字符数; 如果要写入的字符数大于计数,则这些函数返回 -1,指示输出已被截断。