从 Visual Studio 的哪个版本开始,vsnprintf 主要符合标准?

Posted

技术标签:

【中文标题】从 Visual Studio 的哪个版本开始,vsnprintf 主要符合标准?【英文标题】:Starting at what version of Visual Studio is vsnprintf mostly standard-conformant? 【发布时间】:2016-01-25 15:35:14 【问题描述】:

根据Microsoft's documentation for 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 是 vsnprintf 捆绑 C(++) RTL for x86 和 x64 的实现,符合 C99 标准 (ISO/IEC 9899:1999),假设

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

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

    在上述假设下不会产生未定义的行为(包括调用Microsoft's invalid parameter handler); 在buffer==NULLcount==0时返回要写入的长度(不包括终止空字符),从而允许飞行前确定输出的长度; 当buffer!=NULLcount>0 并且返回的结果为非负数时,始终使用终止空字符填充输出字符串,包括由于count 较小而截断的输出。

注意comment:我愿意承认缺少restrict 限定符仍然在允许大部分符合标准的范围内。


文档中关于 (3.) 的一致性模棱两可;据我所知,与 Visual Studio Community 2015 捆绑的实现很好,但并非全部。

如果末尾有空间(即如果要写入的字符数少于count),则缓冲区将以空值终止。

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

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

如果bufferformatNULL,或者如果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 = %ld\n",(long)(_MSC_VER));
#else
    printf("_MSC_VER is undefined\n");
#endif
    printf("f(NULL,0,\"%%d\",777):%3d\n", 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,即使输出为正数。

【问题讨论】:

那不是 vsnprintf 的原型。 来自 C11:int vsnprintf(char * restrict s, size_t n, const char * restrict format, va_list arg); 最值得注意的是,缺少限制指针已经使函数不兼容。 我会说 VS2015 版本看起来更像here 描述的行为:“生成的字符串将以空字符终止,除非 buf_size 为零。” " 如果结果字符串由于 buf_size 限制而被截断,则函数返回如果没有施加限制,将写入的字符总数(不包括终止的空字节)。" .但是,如果您查看示例,MSDN page 似乎不是最新的... 我省略了说明 nullptr 缓冲区会发生什么的部分:“如果 buf_size 为零,则不会写入任何内容,并且缓冲区可能是空指针,但是返回值(将写)仍然计算并返回。”。不确定这部分在VS2015版本中是否正确应用... 【参考方案1】:

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

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

而且你的输出和_vsnprintf一致:

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

【讨论】:

以上是关于从 Visual Studio 的哪个版本开始,vsnprintf 主要符合标准?的主要内容,如果未能解决你的问题,请参考以下文章

visual studio哪个版本好

Visual Studio 2012 和 Blend 哪个版本?

Visual Studio 2012支持哪个XP版本【作为目标操作系统】

Visual Studio 2017 使用哪个版本的 SignTool?

Visual Studio哪个版本最好用,vs最好用的版本事哪个?

有没有办法确定使用哪个版本的 Visual Studio 来编译静态库?