int printf(const char *format, ...);
int fprintf(FILE *stream, 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 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(): _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE; or cc -std=c99
snprintf() と vsnprintf() は最大で size バイトを str に書き込む (size には文字列を終端する aq\0aq もを含まれる)。
vprintf(), vfprintf(), vsprintf(), vsnprintf() の各関数はそれぞれ printf(), fprintf(), sprintf(), snprintf(), の各関数と等価であり、可変数引き数の代わりに va_list を引き数として呼び出される点だけが異なる。 これらの関数では va_end マクロは呼び出されない。 これらの関数は va_arg を呼び出すので、呼び出し後の ap の値は未定義である。 stdarg(3) を参照のこと。
これらの 8 つの関数は format 文字列の制御に従って出力を書き出す。 format 文字列は、これに続く引き数 (または stdarg(3) の可変長引き数機構を使ってアクセスできる引き数) をどのように変換して出力するかを指定する。
C99 と POSIX.1-2001 では、 sprintf(), snprintf(), vsprintf(), vsnprintf() の呼び出しで、範囲が重複するオブジェクト間でコピーが発生する場合の 結果は不定であると規定されている (例えば、出力先の文字列と入力された 引き数の一つが同じバッファを参照している場合などである)。 「注意」の節を参照。
snprintf() と vsnprintf() は、 size バイトを越える文字数を書き込まない (size には文字列を終端する aq\0aq もを含まれる)。 この制限によって出力が切り詰められた場合には、 もし十分なスペースがあれば書き込まれたであろう文字の個数 (文字列を終端する aq\0aq を除く) を返す。 従って、返り値が size 以上だった場合、出力が切り詰められたことを意味する (後述の注意も参照のこと)。
引き数は (型の格上げの後は) 変換指定子が表す型と正確に対応しなければならない。 デフォルトでは、aq*aq や変換指定子が出てくる毎に次の引き数を要求され、 引き数は指定された順序で使用されていく (指定された引き数の個数が不十分ならエラーとなる)。 また、引き数が必要な箇所で aq%aq の代わりに "%m$"、 aq*aqの代わりに "*m$" と書くことで、 明示的にどの引き数を使用するかを指定することもできる。 ここで 10進の整数 m は希望の引き数の引き数リストでの位置を示す (最初の引き数の番号が 1 である)。 従って、以下の二つは等価である。
printf("%*d", width, num);
printf("%2$*1$d", width, num);
は等価である。
二番目の書き方では同じ引き数を繰り返し参照することができる。
C99 標準には、 Single Unix Specification 由来の aq$aq を使った書き方は含まれていない。
aq$aq を使ったスタイルを使うと、引き数を取る変換及び幅と精度の引き数を
全てこのスタイルで指定しなければならないが、
引き数を消費しない "%%" フォーマットと混ざっているかもしれない。
aq$aq で指定される引き数の番号に空きがあってはならない。
例えば、もし引き数 1 と 3 が指定されると、引き数 2 もフォーマット文字列のどこかで
指定されなければならない。
数値変換には小数点や 1000 単位の区切り文字を使うものもある。 実際にどの文字を使うかはロケールの LC_NUMERIC による。 POSIX ロケールでは小数点に aq.aq を用い、 区切り文字は使わない。 従って、
printf("%aq.2f", 1234567.89);
は、 POSIX ロケールでは "1234567.89" 、 nl_NL ロケールでは "1234567,89"、
da_DK ロケールでは "1.234.567,89" となる。
上記の 5 つのフラグは C 標準で定義されている。 SUSv2 では、さらにもう一つフラグ文字が規定されている。
glibc 2.2 では、さらに一つフラグ文字が追加されている。
SUSv2 で長さ修飾子として使用できるのは、 h (hd, hi, ho, hx, hX, hn), l (ld, li, lo, lx, lX, ln, lc, ls), L (Le, LE, Lf, Lg, LG) だけである。
変換指定子とその意味は以下の通りである。
(SUSv2 では、
F
は規定されておらず、無限や NaN に関する文字列表現を
行ってもよいことになっている。
C99 標準では、
f
変換では、無限は "[-]inf" か "[-]infinity" と表示し、
NaN は文字列の先頭に `nan' をつけて表示するように規定されている。
F
変換の場合は "[-]INF", "[-]INFINITY", "NAN*" と表示される。)
l 修飾子が指定されている場合、 引き数は const wchar_t * 型でワイド文字の配列へのポインタであることが期待されている。 配列中のワイド文字は (1文字毎に wcrtomb(3) を呼び出して) マルチバイト文字に変換される (最初のワイド文字の変換の前に wcrtomb() のシフト状態を初期状態に戻してから変換は行われる)。 マルチバイト文字への変換は、文字列を終端する NULL ワイド文字が 出てくるまで行われ、終端 NULL ワイド文字も含めて変換される。 結果のマルチバイト文字列は、終端の NULL バイトが出てくるまで 出力される (終端の NULL バイトは出力されない)。 精度が指定された場合、指定されたバイト数以上には出力されない。 但し、マルチバイト文字の一部分だけが出力されることはない。 精度は「バイト」数を指定するものであり、「ワイド文字」数や 「画面での位置」を指定するものではないことに注意。 精度が指定されていて、さらに出力が配列の末尾に達する前に出力バイト数が 精度の値を超える場合だけは、配列は NULL ワイド文字で終端されていなくてもよい。 それ以外の場合は、必ず配列は NULL ワイド文字で終端されていなければならない。
snprintf() の返り値を見ると、 SUSv2 と C99 標準は互いに矛盾している。 SUSv2 では、 snprintf() が size=0 で呼び出された場合、 1 未満の値を何か返り値とするように規定している。 一方 C99 では、このような場合 str を NULL とし、返り値として (通常通り) 出力バッファが十分な大きさが あった場合に出力されるであろう文字数を返す。
Linux libc4 では、 5 つの C 標準のフラグ、 長さ修飾子 h, l, L、変換 c, d, e, E, f, F, g, G, i, n, o, p, s, u, x, X が使える。 但し F は f と同義である。 また、 D, O, and U を ld, lo, and lu と同じものとして使える (これはまずい仕様で、 後に %D の対応が打ち切られた時に深刻なバグを 引き起こした)。ロケール依存の小数点、1000 区切り、 NaN と無限、 "%m$" と "*m$" は使えない。
Linux libc5 では、 5 つの C 標準のフラグと aq フラグ、ロケール、 "%m$" と "*m$" が使える。 また、長さ修飾子 h, l, L, Z, iand q が使えるが、 L と q は両方とも long double と long long int に対応している (これはバグである)。 現在では変換 F, D, O, U は認識されないが、変換文字 m が追加された。これは strerror(errno) を出力するものである。
glibc 2.0 では、変換文字 C と S が追加された。
glibc 2.1 では、長さ修飾子 hh, j, t, z と変換文字 a, A が追加された。
glibc 2.2 では、 C99 で規定された意味での変換文字 F と フラグ文字 I が追加された。
sprintf(buf, "%s some further text", buf);
しかしながら、標準規格では、 sprintf(), snprintf(), vsprintf(), vsnprintf() の呼び出しにおいて、コピー元とコピー先のバッファが重なっていた場合の 結果は不定である、と明記されている。 使用する gcc(1) のバージョンや指定したコンパイラのオプション次第では、 上記のような呼び出しで、期待した結果が得られ「ない」ことがある。
glibc の snprintf() と vsnprintf() の実装は、バージョン 2.1 以降は C99 標準に準拠しており、 上記の通りの動作をする。 glibc 2.0.6 までは、出力が切り詰められた場合は -1 を返す。
Linux libc4.[45] には snprintf() はないが、 libbsd が提供されており、 その中には sprintf() と等価な (つまり size 引き数を無視する) snprintf() がある。 したがって、初期の libc4 で snprintf() を使うと、深刻なセキュリティ問題を引き起こすことがある。
printf(foo); のようなコードはしばしばバグを引き起こす。 なぜなら foo に % 文字が含まれてるかもしれないからである。 foo が信頼できないユーザー入力から作られている場合には、 その中に %n が含まれていることがあり、 printf() 呼び出し時にメモリへの書き込みが起こり、 セキュリティーホールを作ることになるかもしれない。
#include <math.h> #include <stdio.h> fprintf(stdout, "pi = %.5f\n", 4 * atan(1.0));
日付と時間を "Sunday, July 3, 10:02" の形式で出力する。 (weekday と month は文字列へのポインタである)
#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);
format
はロケールに依存しており、引き数の順番を変えることもできる。
format
が
"%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, ...)
{
/* Guess we need no more than 100 bytes. */
int n, size = 100;
char *p, *np;
va_list ap;
if ((p = malloc(size)) == NULL)
return NULL;
while (1) {
/* Try to print in the allocated space. */
va_start(ap, fmt);
n = vsnprintf(p, size, fmt, ap);
va_end(ap);
/* If that worked, return the string. */
if (n > -1 && n < size)
return p;
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n+1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
if ((np = realloc (p, size)) == NULL) {
free(p);
return NULL;
} else {
p = np;
}
}
}