从Visual Studio的版本开始,vsnprintf大多符合标准
Starting at what version of Visual Studio is vsnprintf mostly standard-conformant?
根据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
引用):
- 在上述假设下不产生未定义的行为(包括调用Microsoft的无效参数处理程序);
- 返回
buffer==NULL
和count==0
时要写入的长度(不包括终止空字符),从而允许预飞行确定输出的长度; - 当
buffer!=NULL
和count>0
时,始终使用终止的 null 字符填充输出字符串,并且返回的结果是非负的,包括由于小count
而截断的输出。
请注意以下评论:我愿意承认缺乏restrict
限定符,因为大多数符合标准的限定符仍在允许范围内。
文档在(3.)方面留下了模糊的一致性;据我所知,与Visual Studio Community 2015捆绑在一起的实现很好,但并非所有都是。
如果末尾有空间(即,如果要写入的字符数小于
count
),则缓冲区将以 null 结尾。
文档的措辞也明确暗示vsnprintf
在buffer==NULL
和count==0
时不符合C99标准(1.)和(2.);但文档的这些部分似乎被证明是错误的:
<小时 />如果要写入的字符数大于
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 = %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,指示输出已被截断。
- 使用CMake检测支持的C++标准
- 如何理解C++标准N3337中的expr.const.cast子句8
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 编译标准库类型
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 铸造标准::有没有回到原来的类型
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 标准库类型的赋值运算符的引用限定符
- 标准是否严格定义了该程序应该如何编译?
- 如何从Windows应用程序输出到标准?
- 安全到标准:移动会员?
- 如何正确将字符串转换为标准::时间::system_clock::time_point?
- 这是否符合C++标准:双响双响,例如!!(-0.0).
- 标准::变体的赋值运算符
- 捕获标准输出以压缩并使用 CTRL-C 中断会给出损坏的 zip 文件
- 如何在 Mac 上使用 c++17 并行标准库算法?
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- 从Visual Studio的版本开始,vsnprintf大多符合标准