从 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==NULL
和count==0
时返回要写入的长度(不包括终止空字符),从而允许飞行前确定输出的长度;
当buffer!=NULL
和count>0
并且返回的结果为非负数时,始终使用终止空字符填充输出字符串,包括由于count
较小而截断的输出。
注意comment:我愿意承认缺少restrict
限定符仍然在允许大部分符合标准的范围内。
文档中关于 (3.) 的一致性模棱两可;据我所知,与 Visual Studio Community 2015 捆绑的实现很好,但并非全部。
如果末尾有空间(即如果要写入的字符数少于
count
),则缓冲区将以空值终止。
文档也有措辞明确暗示vsnprintf
在buffer==NULL
和count==0
时在(1.) 和(2.) 方面不符合C99 标准;但是文档的这些部分似乎是错误的:
如果要写入的字符数大于
count
,这些函数返回-1,表示输出已被截断。如果
buffer
或format
是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 = %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 2012 和 Blend 哪个版本?
Visual Studio 2012支持哪个XP版本【作为目标操作系统】
Visual Studio 2017 使用哪个版本的 SignTool?