書式指定子は、printf には値をどうテキストにするかを、scanf にはテキストをどう値にするかを伝えます。C言語のなかで、既定ではコンパイラが助けてくれない唯一の部分 - 引数の型が関数からは見えない - なので、このページが照合すべき表になります。
指定の一般的な形はこうです。
%[フラグ][幅][.精度][長さ]変換
必須なのは % と変換文字だけです。%-8.2lf は5つのパートすべてを使っています。
変換の表
| 指定子 | 意味 | printf の引数 | scanf の引数 |
|---|---|---|---|
%d | 符号付き10進整数 | int | int * |
%i | 符号付き整数 | int(%d と同じ) | int *(0x、0 始まりの8進も受け付ける) |
%u | 符号なし10進 | unsigned int | unsigned int * |
%f | 10進の浮動小数点 | double | float * |
%F | %f と同じ、INF/NAN が大文字 | double | - |
%e / %E | 指数表記 | double | float * |
%g / %G | %f / %e の短いほう | double | float * |
%a / %A | 16進の浮動小数点 | double | float * |
%c | 1文字 | int(昇格された char) | char * |
%s | 文字列 | char *('\0' 終端) | char *(バッファ) |
%p | ポインタのアドレス | void * | void ** |
%x / %X | 符号なし16進 | unsigned int | unsigned int * |
%o | 符号なし8進 | unsigned int | unsigned int * |
%n | ここまでに書いた文字数を格納 | int * | int * |
%% | % そのもの | なし | % そのものに一致 |
%[...] | 走査集合(scanf のみ) | - | char * |
2つの項目には警告が必要です。%n は表示するのではなくメモリに書き込み、書式文字列脆弱性の仕組みそのものです - ユーザーのテキストを書式文字列に到達させてはいけません。そして %p は void * を要求します。int * を直接渡すのは、現実のどのコンパイラもアドレスを表示してくれるとはいえ未定義です。
長さ修飾子
長さ修飾子は精度と変換文字の間に置かれ、引数が実際にどれだけ大きいかを伝えます。
符号付きの変換(d i)と符号なしの変換(u x o)は異なる型を取ります - %lu が欲しいのは unsigned long であって long ではなく、ここを間違えるのは見た目の問題ではなく未定義動作です。
| 修飾子 | d i と(printf / scanf) | u x o と(printf / scanf) |
|---|---|---|
hh | int(signed char から) / signed char * | unsigned int(unsigned char から) / unsigned char * |
h | int(short から) / short * | unsigned int(unsigned short から) / unsigned short * |
| (なし) | int / int * | unsigned int / unsigned int * |
l | long / long * | unsigned long / unsigned long * |
ll | long long / long long * | unsigned long long / unsigned long long * |
z | size_t の符号付き版 / それへのポインタ | size_t / size_t * |
j | intmax_t / intmax_t * | uintmax_t / uintmax_t * |
t | ptrdiff_t / ptrdiff_t * | ptrdiff_t の符号なし版 |
そして浮動小数点と文字の系統はこうです。
| 修飾子 | 組み合わせ | printf の型 | scanf の型 |
|---|---|---|---|
| (なし) | f e g a | double | float * |
l | f e g a | double(受理されるが効果なし) | double * |
L | f e g a | long double | long double * |
l | c s | wint_t / wchar_t * | wchar_t * |
実際に打ち込むことになる組み合わせはこれです。
覚えるべきは %zu です。 sizeof、strlen、そして標準ライブラリのあらゆるサイズは size_t を返し、64ビットシステムではこれが8バイトなのに対し int は4バイトです。printf("%d", strlen(s)) は、一部のプラットフォームで動いているように見える本物のバグです - %zu を使いましょう。
hh と h の修飾子は、主に格納先のサイズが問題になる scanf のために存在します。printf では short は呼び出し前に int に昇格されるので、どのみち %d で正しく表示されます。%hd は意図を記すだけのものです。
固定幅の型
int32_t のような <stdint.h> の型には、<inttypes.h> のマクロが移植性のある指定子になります。
PRId32 はプラットフォームに応じた正しい文字に展開され、隣接する文字列リテラルは連結されます。だから書式が3つに分かれているのです。醜いのは確かで、代替案 - long long にキャストして %lld を使う - のほうが読みやすいことが多く、しかも常に正しく動きます。
フラグ
フラグは % の直後に置かれ、見た目を変えます。printf にのみ適用されます。
| フラグ | 効果 | 例 | 出力 |
|---|---|---|---|
- | 値の前ではなく後ろを詰める | 42 に %-6d| | 42 | |
+ | 符号付きの値に常に符号を付ける | 42 に %+d | +42 |
| 空白 | + が入る位置に空白を置く | 42 に % d | 42 |
0 | 空白ではなくゼロで詰める | 42 に %06d | 000042 |
# | 別形式:%x に 0x、%o に 0、%g で小数点を残す | 255 に %#x | 0xff |
フラグは組み合わせられます。%-+8.2f は最小幅8、小数2桁、常に符号付き、数値の後ろを詰める、という意味です。両方書いた場合は - が 0 に勝ちます。
幅と精度
幅は最小のフィールドサイズで、そこに届くまで詰められますが、収めるために切り詰められることは決してありません。精度(ドットの後)は、変換の系統ごとに意味が異なります。
| 変換 | 精度の意味 |
|---|---|
%f %e | 小数点以下の桁数(既定は6) |
%g | 有効数字の総数(既定は6) |
%s | 表示する最大文字数 |
%d %i %u %x %o | 最小の桁数、ゼロ詰め |
どちらの数値も * に置き換えると、値そのものより先に読まれる int の引数からその数を取ります。列幅を実行時に計算する表を組み立てるのはこの方法です。
scanf では、幅にまったく別の、そしてはるかに重要な役目があります。変換がどれだけ入力を消費するかを制限するのです。これが %s とバッファオーバーフローの間に立つ唯一のものです。
printf と scanf が食い違うところ
よく似ていながら、4か所で食い違います。そのどれもが現役のバグの源です。
| 状況 | printf | scanf |
|---|---|---|
double の値 | %f(float は double に昇格) | %lf - %f は float * の意味 |
| 幅 | 最小フィールドサイズ、詰める | 消費する入力の最大、切り詰める |
| 引数 | 値 | ポインタ - &x、または配列名 |
| 書式中の空白 | 空白を表示する | 連続する空白類を読み飛ばす |
2.5 hello を試してみてください。それを読む書式と表示する書式は、同じ double に対して違う文字を使っており、%15s の 15 は入力を15文字+終端文字に制限します - ちょうど word のサイズです。
scanf の走査集合
%[...] は、空白で区切られた単語ではなく文字の集合を読みます。scanf で行全体を読む方法ですが、fgets のほうが依然として良い道具です。
[^\n] は「改行以外の任意の文字」という意味です。%[0-9] なら数字だけ、%[abc] ならその3文字だけを読みます。%s とは違い、走査集合は先頭の空白類を読み飛ばしません。そのためバッファに改行が残っていると、何にも一致しなくなります。
間違えれば未定義動作
printf は引数の本当の型を見られません。指定子が示すバイト数だけを、指定子が言うとおりに解釈して読みます。ですから不一致は書式の乱れではなく、メモリを別のものとして読むことです。
printf("%d\n", 3.5); /* ゴミを表示:double のビットを int として読む */
printf("%s\n", 42); /* 42 をアドレスとして扱う - クラッシュ */
printf("%d\n", strlen(s)); /* size_t を int として読む - 64ビットでは誤り */
printf("%f\n", 3); /* int を double として読む - ゴミ */
防御にかかるのはフラグ1つです。-Wall を付けてコンパイルすれば、GCCはすべてのリテラルな書式文字列をその引数と照合します。
gcc -Wall -Wextra program.c -o program
上の4行はいずれもコンパイル時に報告されます。これはC言語で最も価値の高い警告であり、上のエディタでは既定で有効になっています。
これらを実際に使う側面 - 桁の揃った表、浮動小数点を読みやすく表示する方法、古典的な改行バグなしに入力を読む方法 - については printf と scanf を見てください。
よくある質問
C言語の %d はどういう意味ですか?
%d は符号付き10進整数 - つまり int - の変換指定子です。printf では引数を10進の数字として表示し、scanf では10進の数字を読んで int * に格納します。printf では %i も同じ意味ですが、scanf の %i は 0x の16進や先頭ゼロの8進も受け付けます。
C言語の %f と %lf の違いは何ですか?
printf では実用上の違いはありません。float の引数は double に昇格されるので %f は両方を表示し、C99は %lf を同義語として受け入れます。scanf では違いが決定的です - %f は float *(4バイト)に、%lf は double *(8バイト)に書き込みます。double に %f を使うとメモリが壊れます。
C言語で size_t や long long を表示するには?
size_t(sizeof や strlen が返すもの)には %zu、long long / unsigned long long には %lld / %llu を使います。これらに %d を使うのは未定義動作です - 32ビットシステムではたまたま動き、64ビットではおかしくなるので、このバグは長く生き延びます。
C言語で間違った書式指定子を使うとどうなりますか?
丸め誤差ではなく、未定義動作になります。printf は指定子が主張するとおりに引数を読むので、printf("%d", 3.5) はゴミを表示し、printf("%s", 42) は数値の 42 をアドレスとしてたどって、たいていクラッシュします。-Wall を付けてコンパイルすれば、GCCが書式文字列と引数を照合してくれます。