vsnprintf
vprintf, vfprintf, vsprintf, vsnprintf, vprintf_s, vfprintf_s
在标题中定义 | ||
---|---|---|
(1) | ||
int vprintf( const char *format, va_list vlist | (直到C99) | |
int vprintf( const char *restrict format, va_list vlist | (自C99以来) | |
(2) | ||
int vfprintf ( FILE * stream , const char * format , va_list vlist ); | (直到C99) | |
int vfprintf ( FILE * restrict stream , const char * restrict format , va_list vlist ); | (自C99以来) | |
(3) | ||
int vsprintf ( char * buffer , const char * format , va_list vlist ); | (直到C99) | |
int vsprintf ( char * restrict buffer , const char * restrict format , va_list vlist ); | (自C99以来) | |
int vsnprintf ( char * restrict buffer , int bufsz , const char * restrict format , va_list vlist ); | (4) | (自C99以来) |
int vprintf_s ( const char * restrict format , va_list arg ); | (5) | (自C11以来) |
int vfprintf_s ( FILE * restrict stream , const char * restrict format , va_list arg ); | (6) | (自C11以来) |
int vsprintf_s ( char * restrict buffer , rsize_t bufsz , const char * restrict format , va_list arg ); | (7) | (自C11以来) |
int vsnprintf_s (char * restrict buffer , rsize_t bufsz , const char * restrict format , va_list arg ); | (8) | (自C11以来) |
从定义的位置加载数据vlist
,将它们转换为等同字符串并将结果写入各种接收器。
1)将结果写入stdout
。
2)将结果写入文件流stream
。
3)将结果写入字符串buffer
。
4)将结果写入字符串buffer
。最多的buf_size
字符是写入的。结果字符串将以空字符结尾,除非buf_size
为零。如果buf_size
为零,则不会写入任何内容,并且buffer
可能是空指针,但返回值(将写入的字节数)仍然会计算并返回。
5-8)与(1-4)相同,只是在运行时检测到以下错误并调用当前安装的约束处理函数:
- 转换说明符
%n
存在于format
由于所有的边界检查功能,vprintf_s,vfprintf_s,vsprintf_s,和vsnrintf_s仅保证可供如果__STDC_LIB_EXT1__由实现所定义,并且如果用户定义__STDC_WANT_LIB_EXT1__的整数常数1,包括之前<stdio.h>。
参数
流 | - | 输出文件流写入 |
---|---|---|
缓冲 | - | 指向要写入的字符串的指针 |
bufsz | - | 最多bufsz - 可能会写入1个字符,再加上空终止符 |
格式 | - | 指向以空字符结尾的字符串的指针,指定如何解释数据。 |
格式字符串由普通的多字节字符(除外%)组成,它们被不变地复制到输出流和转换规范中。每个转换规范具有以下格式: | ||
介绍%人物 | ||
(可选)一个或多个修改转换行为的标志: | ||
-:转换的结果在字段内左对齐(默认情况下它是右对齐的) | ||
+:带符号转换的符号总是预设为转换结果的前缀(默认情况下结果前面为减号,仅当它为负值时) | ||
空格:如果签名转换的结果不是以符号字符开头,或者是空的,则空格会预设为结果。如果+标志存在,它将被忽略。 | ||
#:执行转换的替代形式。请参阅下表以了解确切的效果,否则行为未定义。 | ||
0:对于整数和浮点数转换,前导零用于填充字段而不是空格字符。对于整数,如果明确指定了精度,它将被忽略。对于使用此标志的其他转换会导致未定义的行为。如果-标志存在,它将被忽略。 | ||
(可选)整数值或*指定最小字段宽度。如果需要,结果会填充空格字符(默认情况下),右侧对齐时填充空白字符,左侧填充右侧填充。在使用的情况下*,宽度由类型的附加参数指定int。如果参数的值是负数,则结果是-指定的标志和正的字段宽度。(注意:这是最小宽度:该值从不被截断。) | ||
(可选) .后面跟随整数或者*或者既不指定转换的精度。在使用的情况下*,精度由类型的附加参数指定int。如果这个参数的值是负数,它将被忽略。如果既不使用数字也不*使用,则精度取为零。请参阅下表以了解精确度的确切影响。 | ||
(可选) 长度修饰符,用于指定参数的大小 | ||
转换格式说明符 | ||
以下格式说明符可用: | ||
转换 | ||
说明符 | ||
长度修饰符 | ||
% | ||
c | ||
s | ||
d | ||
i | ||
o | ||
x | ||
X | ||
u | ||
f | ||
F | ||
e | ||
E | ||
a | ||
A | ||
(C99)。 | ||
g | ||
G | ||
n | ||
p | ||
浮点转换函数将无穷大转换为inf或infinity。使用哪一个是实现定义的。 | ||
非数字转换为nan或。使用哪一个是实现定义的。nan(char_sequence) | ||
该转换F,E,G,A输出INF,INFINITY,NAN来代替。 | ||
即使%c需要int参数,通过char调用可变参数函数时发生的整数提升也是安全的。 | ||
对于固定宽度的字符类型(正确的转换规格int8_t,等等)都在头中定义<inttypes.h>还(虽然PRIdMAX,PRIuMAX等是同义词%jd,%ju等)。 | ||
内存写入转换说明符%n是安全漏洞的常见目标,其中格式字符串取决于用户输入,并且不受边界检查printf_s函数族的支持。 | ||
每个转换说明符的操作之后都有一个序列点 ; 这允许将多个%n结果存储在相同的变量中,或者作为边缘情况,%n在相同的调用中打印由较早修改的字符串。 | ||
如果转换规范无效,则行为未定义。 | ||
VLIST | - | 包含要打印的数据的变量参数列表 |
返回值
1-3)如果发生错误,则写入的字符数如果成功或为负值。
4)如果发生错误,则成功写入字符数或写入负值。如果由于buf_size
限制而导致结果字符串被截断,则函数将返回如果未施加该限制的情况下将被写入的字符总数(不包括终止空字节)。
5,6)传输到输出流的字符数或负值(如果发生输出错误,运行时间约束违规错误或编码错误)。
7)写入的字符数buffer
,不包括空字符(只要buffer
不是空指针,bufsz
并且不为零且不大于RSIZE_MAX
),则不计入空字符,或者在运行时约束违规时为零,编码错误为负值
8)不包括终止空字符的字符数(只要buffer
不是空指针并且bufsz
不为零且不大于RSIZE_MAX
),buffer
如果bufsz
被忽略,将被写入的字符数或者如果运行时约束违规或编码错误发生
注意
所有这些函数va_arg
至少调用一次,arg
返回后的值是不确定的。这些函数不会调用va_end
,并且它必须由调用者完成。
vsnprintf_s
不像vsprintf_s
,会截断结果以适应指向的数组buffer
。
例
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
void debug_log(const char *fmt, ...)
{
struct timespec ts;
timespec_get(&ts, TIME_UTC
char time_buf[100];
size_t rc = strftime(time_buf, sizeof time_buf, "%D %T", gmtime(&ts.tv_sec)
snprintf(time_buf + rc, sizeof time_buf - rc, ".%06ld UTC", ts.tv_nsec / 1000
va_list args1;
va_start(args1, fmt
va_list args2;
va_copy(args2, args1
char buf[1+vsnprintf(NULL, 0, fmt, args1)];
va_end(args1
vsnprintf(buf, sizeof buf, fmt, args2
va_end(args2
printf("%s [debug]: %s\n", time_buf, buf
}
int main(void)
{
debug_log("Logging, %d, %d, %d", 1, 2, 3
}
可能的输出:
02/20/15 21:58:09.072683 UTC [debug]: Logging, 1, 2, 3