语法

#include <stdio.h>

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

#include <stdarg.h>

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vdprintf(int fd, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

glibc的功能测试宏要求(请参阅feature_test_macros(7)):

snprintf(),vsnprintf():

_XOPEN_SOURCE>= 500 || _ISOC99_SOURCE || || / * Glibc版本

dprintf(),vdprintf():

Since glibc 2.10:
_POSIX_C_SOURCE>= 200809L
Before glibc 2.10:
_GNU_SOURCE

遵循规范

fprintf(),printf(),sprintf(),vprintf(),vfprintf(),vsprintf():POSIX.1-2001,POSIX.1-2008,C89,C99。

snprintf(),vsnprintf():POSIX.1-2001,POSIX.1-2008,C99。

dprintf()和vdprintf()函数最初是GNU扩展,后来在POSIX.1-2008中进行了标准化。

关于snprintf()的返回值,SUSv2和C99彼此矛盾:当使用size = 0调用snprintf()时,SUSv2规定了未指定的返回值,小于1,而在这种情况下C99允许str为NULL,并给出返回值(一如既往)是在输出字符串足够大的情况下要写入的字符数。 POSIX.1-2001及更高版本将其snprintf()的规范与C99对齐。

glibc 2.1添加了长度修饰符hhj,t和z以及转换字符a和A。

glibc 2.2添加具有C99语义的转换字符F和标志字符I。

备注

一些程序不谨慎地依赖于以下代码

sprintf(buf,"%s一些进一步的文本",buf);

将文本追加到buf。但是,标准明确指出,如果在调用sprintf(),snprintf(),vsprintf()和vsnprintf()时源缓冲区和目标缓冲区重叠,则结果不确定。根据所用gcc(1)的版本以及所使用的编译器选项,上述调用不会产生预期的结果。

从glibc 2.1版开始,函数snprintf()和vsnprintf()的glibc实现符合C99标准,即,其行为如上所述。直到glibc 2.0.6,当输出被截断时,它们将返回-1。

PRINTF - Linux手册页

Linux程序员手册 第3部分
更新日期: 2020-06-09

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/

返回值

成功返回后,这些函数将返回打印的字符数(不包括用于结束输出到字符串的空字节)。

函数snprintf()和vsnprintf()写入的字节数不超过大小字节(包括终止的空字节(aq \ 0aq))。如果由于此限制而输出被截断,则返回值是如果有足够的空间,将被写入最终字符串的字符数(不包括终止的空字节)。因此,size或更大的返回值意味着输出被截断。 (另请参见下面的注释。)

如果遇到输出错误,则返回负值。

名称

printf,fprintf,dprintf,sprintf,snprintf,vprintf,vfprintf,vdprintf,vsprintf,vsnprintf-格式化输出转换

说明

printf()系列中的函数根据如下所述的格式产生输出。函数printf()和vprintf()将输出写入标准输出流stdout。 fprintf()和vfprintf()将输出写入给定的输出流; sprintf(),snprintf(),vsprintf()和vsnprintf()写入字符串str。

函数dprintf()与fprintf()相同,只不过它输出到文件描述符fd而不是stdio流。

函数snprintf()和vsnprintf()最多将大小字节(包括终止的空字节(aq \ 0aq))写入str。

函数vprintf(),vfprintf(),vdprintf(),vsprintf(),vsnprintf()分别等效于函数printf(),fprintf(),dprintf(),sprintf(),snprintf()。使用va_list而不是可变数量的参数来调用它们。这些函数不调用va_end宏。由于它们调用va_arg宏,因此在调用之后ap的值是不确定的。参见stdarg(3)。

所有这些函数都在格式字符串的控制下写入输出,该格式字符串指定如何转换后续参数(或通过stdarg(3)的可变长度参数工具访问的参数)以输出。

C99和POSIX.1-2001指定如果对sprintf(),snprintf(),vsprintf()或vsnprintf()的调用将导致在重叠的对象之间进行复制(例如,如果目标字符串),则结果是不确定的数组和提供的输入参数之一引用相同的缓冲区)。请参阅注释。

Format of the format string

格式字符串是一个字符串,以其初始移位状态(如果有)开始和结束。格式字符串由零个或多个指令组成:普通字符(不是%),原样复制到输出流;和转换规范,每个规范都会导致获取零个或多个后续参数。每个转换说明都以字符%开头,并以转换说明符结尾。在两者之间可能有(按此顺序)零个或多个标志,可选的最小字段宽度,可选的精度和可选的长度修饰符。

参数必须与转换说明符正确对应(在类型提升之后)。默认情况下,参数以给定的顺序使用,其中每个aq * aq(请参见下面的"字段宽度"和"精度")和每个转换说明符都要求下一个参数(如果给出的参数太多,则会出错)。通过写"%m $"而不是aq%aq和" * m $"而不是aq * aq,还可以明确指定在需要参数的每个位置采用哪个参数,其中十进制整数m表示所需参数的参数列表中的位置,从1开始索引。因此,

printf("%*d", width, num);

printf("%2$*1$d", width, num);

是等效的。第二种样式允许重复引用相同的参数。 C99标准不包括来自Single UNIX规范的使用aq $ aq的样式。如果使用使用aq $ aq的样式,则必须在所有带参数的转换以及所有宽度和精度参数中使用该样式,但是它可以与不使用参数的" %%"格式混合使用。使用aq $ aq指定的参数数量可能没有空格。例如,如果指定了参数1和3,则还必须在格式字符串的某处指定参数2。

对于某些数字转换,将使用基数字符("小数点")或数千个分组字符。实际使用的字符取决于语言环境的LC_NUMERIC部分。 (请参阅setlocale(3)。)POSIX语言环境使用aq.aq作为基数字符,并且没有分组字符。从而,

printf("%aq.2f", 1234567.89);

在POSIX语言环境中为" 1234567.89",在nl_NL语言环境中为" 1234567,89",在da_DK语言环境中为" 1.234.567,89"。

Flag characters

字符%后跟零个或多个以下标志:

#
该值应转换为"替代形式"。对于o转换,将输出字符串的第一个字符设置为零(如果还不为零,则将其前缀为0)。对于x和X转换,非零结果前面带有字符串" 0x"(对于X转换,则为" 0X")。对于aaeeff,g和G转换,即使没有数字跟随,结果也始终包含小数点(通常,只有当a,位数字)。对于g和G转换,不会从结果中删除尾随零,否则会从中去除。对于其他转换,结果是不确定的。
0
该值应为零填充。对于diouxxaaeeff,g和G转换,转换后的值在左侧填充零而不是空白。如果同时出现0和-标志,则忽略0标志。如果通过数字转换(diou,x和X)给出精度,则忽略0标志。对于其他转换,行为是不确定的。
-
转换后的值将在场边界上进行左调整。 (默认值为右对齐。)转换后的值在右侧用空格填充,而不是在左侧用空格或零填充。如果同时给出-,则-会覆盖0。
aq aq
(空格)在带符号的转换产生的正数(或空字符串)之前应留一个空格。
+
应始终在带符号的转换产生的数字之前放置一个符号(+或-)。默认情况下,符号仅用于负数。如果同时使用+,则+会覆盖空格。

上面的五个标志字符在C99标准中定义。单一UNIX规范还指定了另一个标志字符。

aq
对于十进制转换(iduffg,G),如果语言环境信息指示任何输出,则输出将与数千个分组字符分组。 (请参阅setlocale(3)。)请注意,许多版本的gcc(1)无法解析此选项,并且会发出警告。 (SUSv2不包括%aqF,但是SUSv3添加了它。)

glibc 2.2添加了另一个标志字符。

I
对于十进制整数转换(id,u),输出使用语言环境的替代输出数字(如果有)。例如,由于glibc 2.2.3,这将在波斯语(" fa_IR")语言环境中给出阿拉伯语-印度数字。

Field width

可选的十进制数字字符串(第一位非零)指定最小字段宽度。如果转换后的值的字符数少于字段宽度,它将在左边(或右边,如果已给定了左调整标志)的位置上填充空格。代替十进制数字字符串,可以写" *"或" * m $"(对于某些十进​​制整数m)来指定字段宽度分别在下一个参数或第m个参数中给出,这必须属于int类型。负场宽度用作aq-aq标志,后跟正场宽度。在任何情况下,不存在或较小的字段宽度都不会导致字段被截断;如果转换结果比字段宽度宽,则将字段扩展为包含转换结果。

Precision

可选精度,以句点(aq.aq)形式,后跟可选的十进制数字字符串。代替十进制数字字符串,可以写" *"或" * m $"(对于某些十进​​制整数m)来指定精度分别在下一个参数或第m个参数中给出。类型为int。如果给定的精度仅为aq.aq,则精度为零。负精度就像忽略精度一样。这给出了diou,x和X转换出现的最小位数,aaee,f和F转换的基数字符后出现的位数, g和G转换的最大有效位数,或s和S转换的字符串中要打印的最大字符数。

Length modifier

在此,"整数转换"代表diou,x或X转换。

hh
接下来的整数转换对应于有符号char或无符号char参数,或者随后的n转换对应于有符号char参数的指针。
h
后续的整数转换对应于short int或unsigned short int参数,或者后续的n转换对应于short int参数的指针。
l
(ell)后续的整数转换对应于long int或unsigned long int参数,或者后续的n转换对应于long int参数的指针,或者后续的c转换对应于wint_t参数,或者后续的s转换对应指向wchar_t参数的指针。
ll
(好吧)。后续的整数转换对应于long long int或unsigned long long int参数,或者后续的n转换对应于long long int参数的指针。
q
ll的同义词。这是源自BSD的非标准扩展;避免在新代码中使用它。
L
aaeeff,g或G转换之后的是一个长double自变量。 (C99允许%LF,但SUSv2不允许。)
j
后面的整数转换对应于intmax_t或uintmax_t参数,或者后面的n转换对应于指向intmax_t参数的指针。
z
后面的整数转换对应于size_t或ssize_t参数,或者后面的n转换对应于指向size_t参数的指针。
Z
z的非标准同义词,早于z的出现。不要在新代码中使用。
t
后面的整数转换对应于ptrdiff_t参数,或者后面的n转换对应于指向ptrdiff_t参数的指针。

SUSv3指定以上所有内容,但明确指出是非标准扩展的那些修饰符除外。 SUSv2仅指定长度修饰符h(在hdhihohxhx,hn中)和l(在ldlilolxlxlnlc,ls中)和l(在LeLe,Lf中) ,Lg,LG)。

作为非标准扩展,GNU实现将ll和L视为同义词,因此可以例如编写llg(作为符合标准的Lg的同义词)和Ld(作为符合标准的lld的同义词)。这种用法是不可移植的。

Conversion specifiers

一个字符,指定要应用的转换类型。转换说明符及其含义是:

d, i
int参数将转换为带符号的十进制表示法。精度(如果有的话)给出了必须出现的最小位数。如果转换后的值需要较少的数字,则在左边用零填充。默认精度为1。当以显式精度0打印0时,输出为空。
o, u, x, X
无符号int参数将转换为无符号八进制(o),无符号十进制(u)或无符号十六进制(x和X)表示法。字母abcdef用于x转换;字母ABCDEF用于X转换。精度(如果有的话)给出了必须出现的最小位数。如果转换后的值需要较少的数字,则在左边用零填充。默认精度为1。以显式精度0打印0时,输出为空。
e, E
将double参数四舍五入并转换为[-] d样式。 ddd e±dd,其中小数点字符前有一位数字(如果参数为非零,则为非零),其后的位数与精度相等;如果精度丢失,则取为6;如果精度为零,则不显示小数点字符。 E转换使用字母e(而不是e)来引入指数。指数始终至少包含两位数字;如果值为零,则指数为00。
f, F
将double参数四舍五入并转换为[-] ddd格式的十进制表示法。 ddd,其中小数点后的位数等于精度规格。如果缺少精度,则取为6;否则,取值为0。如果精度明确为零,则不显示小数点字符。如果出现小数点,则在其前面至少出现一位数字。
(SUSv2不了解F,并说可以提供无穷大和NaN的字符串表示形式。SUSv3添加了F的规范。C99标准为无穷大指定了[[]] inf或" [-] infinity",并且如果是f转换,则以" nan"为NaN开头的字符串,如果是F转换,则以" [-] INF"或" [-] INFINITY"或" NAN"开头。)
g, G
double参数将转换为f或e样式(对于G转换,则转换为F或E)。精度指定有效位数。如果缺少精度,则给出6位数字。如果精度为零,则将其视为1。如果转换后的指数小于-4或大于或等于精度,则使用样式e。尾随零从结果的分数部分中删除;只有在小数点后跟至少一位数字时,小数点才会出现。
a, A
(C99;不是在SUSv2中,而是在SUSv3中添加的。)为了进行转换,将double参数转换为[-] 0x h样式的十六进制表示法(使用字母abcdef)。 hhhh p±;对于A转换,使用前缀0x,字母ABCDEF和指数分隔符P。小数点前有一个十六进制数字,其后的位数等于精度。如果存在以2为底的精确表示形式,则默认精度足以满足该值的精确表示形式;否则,该缺省精度足够大以区分double类型的值。对于未归一化的数字,未指定小数点前的数字;对于归一化数字,未指定小数点之前的数字。
c
如果不存在l修饰符,则将int参数转换为无符号字符,并写入结果字符。如果存在l修饰符,则通过调用wcrtomb(3)函数将wint_t(宽字符)参数转换为多字节序列,转换状态从初始状态开始,并写入结果的多字节字符串。
s
如果不存在l修饰符:则const char *参数应该是指向字符类型数组的指针(指向字符串的指针)。数组中的字符被写到(但不包括)终止空字节(aq \ 0aq);如果指定了精度,则最多写入指定的数字。如果给出精度,则不需要空字节。如果未指定精度或大于数组的大小,则数组必须包含一个终止的空字节。
如果存在l修饰符:则const wchar_t *参数应为指向宽字符数组的指针。数组中的宽字符被转换为多字节字符(每个通过调用wcrtomb(3)函数,转换状态从第一个宽字符之前的初始状态开始),直至并包括一个终止的空宽字符。产生的多字节字符被写入(但不包括)终止空字节。如果指定了精度,则写入的字节数不超过指定的数字,但不写入部分多字节字符。请注意,精度决定写入的字节数,而不是宽字符或屏幕位置的数目。除非给出精度,否则数组必须包含一个终止的空宽字符,该精度太小以至于在到达数组末尾之前写入的字节数超过了它。
C
(不在C99或C11中,但在SUSv2,SUSv3和SUSv4中。)lc的同义词。不要用
S
(不在C99或C11中,但在SUSv2,SUSv3和SUSv4中。)ls的同义词。不要用
p
void *指针参数以十六进制打印(就像用%#x或%#lx一样)。
n
到目前为止写入的字符数存储在相应参数所指向的整数中。该参数应为int *或其大小与(可选)提供的整数长度修饰符匹配的变体。没有参数被转换。 (仿生C库不支持此说明符。)如果转换说明包含任何标志,字段宽度或精度,则该行为不确定。
m
(Glibc扩展;由uClibc和musl支持。)打印strerror(errno)的输出。不需要参数。
%
会写一个aq%aq。没有参数被转换。完整的转换规范为aq %% aq。

另外参见

printf(1),asprintf(3),puts(3),scanf(3),setlocale(3),strfromd(3),wcrtomb(3),wprintf(3),locale(5)

BUGS

由于sprintf()和vsprintf()假定使用任意长的字符串,因此调用者必须注意不要溢出实际空间。这通常是无法保证的。请注意,生成的字符串的长度取决于语言环境,并且难以预测。请改用snprintf()和vsnprintf()(或asprintf(3)和vasprintf(3))。

诸如printf(foo);的代码通常会指示错误,因为foo可能包含%字符。如果foo来自不受信任的用户输入,则它可能包含%n,从而导致printf()调用写入内存并创建安全漏洞。

示例

要将Pi打印到小数点后五个位:

#include <math.h>
#include <stdio.h>
fprintf(stdout, "pi = %.5f\n", 4 * atan(1.0));

以" 7月3日,星期日,10:02"格式打印日期和时间,其中工作日和月份是指向字符串的指针:

#include <stdio.h>
fprintf(stdout, "%s, %s %d, %.2d:%.2d\n",
        weekday, month, day, hour, min);

许多国家/地区使用日-月-年顺序。因此,国际化版本必须能够按照格式指定的顺序打印参数:

#include <stdio.h>
fprintf(stdout, format,
        weekday, month, day, hour, min);

格式取决于语言环境,并且可能置换参数。带有值:

"%1$s, %3$d. %2$s, %4$d:%5$.2d\n"

一个人可能会获得" Sonntag,3。Juli,10:02"。

要分配足够大的字符串并打印到其中(对于glibc 2.0和glibc 2.1都正确的代码):

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

char *
make_message(const char *fmt, ...)
{
    int n = 0;
    size_t size = 0;
    char *p = NULL;
    va_list ap;

    /* Determine required size */

    va_start(ap, fmt);
    n = vsnprintf(p, size, fmt, ap);
    va_end(ap);

    if (n < 0)
        return NULL;

    /* One extra byte for '##代码##' */

    size = (size_t) n + 1;
    p = malloc(size);
    if (p == NULL)
        return NULL;

    va_start(ap, fmt);
    n = vsnprintf(p, size, fmt, ap);
    va_end(ap);

    if (n < 0) {
        free(p);
        return NULL;
    }

    return p;
}

如果在2.0.6之前的glibc版本中发生截断,则将其视为错误,而不是进行适当处理。

属性

有关本节中使用的术语的说明,请参见attribute(7)。

InterfaceAttributeValue
printf(),fprintf(),
sprintf(),snprintf(),
vprintf(),vfprintf(),
vsprintf(),vsnprintf()
Thread safetyMT-Safe locale
日期:2019-08-20 18:00:08 来源:oir作者:oir