Menu

C言語の書式指定子:printf と scanf の完全リファレンス

C言語のすべての書式指定子を一か所に - %d、%s、%c、%f、%p などの意味、長さ修飾子 l・ll・h・z、幅・精度・フラグ、そして printf と scanf で異なるもの。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

書式指定子は、printf には値をどうテキストにするかを、scanf にはテキストをどう値にするかを伝えます。C言語のなかで、既定ではコンパイラが助けてくれない唯一の部分 - 引数の型が関数からは見えない - なので、このページが照合すべき表になります。

指定の一般的な形はこうです。

%[フラグ][幅][.精度][長さ]変換

必須なのは % と変換文字だけです。%-8.2lf は5つのパートすべてを使っています。

変換の表

指定子意味printf の引数scanf の引数
%d符号付き10進整数intint *
%i符号付き整数int%d と同じ)int *0x0 始まりの8進も受け付ける)
%u符号なし10進unsigned intunsigned int *
%f10進の浮動小数点doublefloat *
%F%f と同じ、INF/NAN が大文字double-
%e / %E指数表記doublefloat *
%g / %G%f / %e の短いほうdoublefloat *
%a / %A16進の浮動小数点doublefloat *
%c1文字int(昇格された charchar *
%s文字列char *'\0' 終端)char *(バッファ)
%pポインタのアドレスvoid *void **
%x / %X符号なし16進unsigned intunsigned int *
%o符号なし8進unsigned intunsigned int *
%nここまでに書いた文字数を格納int *int *
%%% そのものなし% そのものに一致
%[...]走査集合(scanf のみ)-char *

2つの項目には警告が必要です。%n は表示するのではなくメモリに書き込み、書式文字列脆弱性の仕組みそのものです - ユーザーのテキストを書式文字列に到達させてはいけません。そして %pvoid * を要求します。int * を直接渡すのは、現実のどのコンパイラもアドレスを表示してくれるとはいえ未定義です。

長さ修飾子

長さ修飾子は精度と変換文字の間に置かれ、引数が実際にどれだけ大きいかを伝えます。

符号付きの変換(d i)と符号なしの変換(u x o)は異なる型を取ります - %lu が欲しいのは unsigned long であって long ではなく、ここを間違えるのは見た目の問題ではなく未定義動作です。

修飾子d i と(printf / scanf)u x o と(printf / scanf)
hhintsigned char から) / signed char *unsigned intunsigned char から) / unsigned char *
hintshort から) / short *unsigned intunsigned short から) / unsigned short *
(なし)int / int *unsigned int / unsigned int *
llong / long *unsigned long / unsigned long *
lllong long / long long *unsigned long long / unsigned long long *
zsize_t の符号付き版 / それへのポインタsize_t / size_t *
jintmax_t / intmax_t *uintmax_t / uintmax_t *
tptrdiff_t / ptrdiff_t *ptrdiff_t の符号なし版

そして浮動小数点と文字の系統はこうです。

修飾子組み合わせprintf の型scanf の型
(なし)f e g adoublefloat *
lf e g adouble(受理されるが効果なし)double *
Lf e g along doublelong double *
lc swint_t / wchar_t *wchar_t *

実際に打ち込むことになる組み合わせはこれです。

覚えるべきは %zu です。 sizeofstrlen、そして標準ライブラリのあらゆるサイズは size_t を返し、64ビットシステムではこれが8バイトなのに対し int は4バイトです。printf("%d", strlen(s)) は、一部のプラットフォームで動いているように見える本物のバグです - %zu を使いましょう。

hhh の修飾子は、主に格納先のサイズが問題になる 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 に %06d000042
#別形式:%x0x%o0%g で小数点を残す255 に %#x0xff

フラグは組み合わせられます。%-+8.2f は最小幅8、小数2桁、常に符号付き、数値の後ろを詰める、という意味です。両方書いた場合は -0 に勝ちます。

幅と精度

は最小のフィールドサイズで、そこに届くまで詰められますが、収めるために切り詰められることは決してありません。精度(ドットの後)は、変換の系統ごとに意味が異なります。

変換精度の意味
%f %e小数点以下の桁数(既定は6)
%g有効数字の総数(既定は6)
%s表示する最大文字数
%d %i %u %x %o最小の桁数、ゼロ詰め

どちらの数値も * に置き換えると、値そのものより先に読まれる int の引数からその数を取ります。列幅を実行時に計算する表を組み立てるのはこの方法です。

scanf では、幅にまったく別の、そしてはるかに重要な役目があります。変換がどれだけ入力を消費するかを制限するのです。これが %s とバッファオーバーフローの間に立つ唯一のものです。

printf と scanf が食い違うところ

よく似ていながら、4か所で食い違います。そのどれもが現役のバグの源です。

状況printfscanf
double の値%f(float は double に昇格)%lf - %ffloat * の意味
最小フィールドサイズ、詰める消費する入力の最大、切り詰める
引数ポインタ - &x、または配列名
書式中の空白空白を表示する連続する空白類を読み飛ばす

2.5 hello を試してみてください。それを読む書式と表示する書式は、同じ double に対して違う文字を使っており、%15s15 は入力を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 の %i0x の16進や先頭ゼロの8進も受け付けます。

C言語の %f と %lf の違いは何ですか?

printf では実用上の違いはありません。float の引数は double に昇格されるので %f は両方を表示し、C99は %lf を同義語として受け入れます。scanf では違いが決定的です - %ffloat *(4バイト)に、%lfdouble *(8バイト)に書き込みます。double に %f を使うとメモリが壊れます。

C言語で size_t や long long を表示するには?

size_tsizeofstrlen が返すもの)には %zulong long / unsigned long long には %lld / %llu を使います。これらに %d を使うのは未定義動作です - 32ビットシステムではたまたま動き、64ビットではおかしくなるので、このバグは長く生き延びます。

C言語で間違った書式指定子を使うとどうなりますか?

丸め誤差ではなく、未定義動作になります。printf は指定子が主張するとおりに引数を読むので、printf("%d", 3.5) はゴミを表示し、printf("%s", 42) は数値の 42 をアドレスとしてたどって、たいていクラッシュします。-Wall を付けてコンパイルすれば、GCCが書式文字列と引数を照合してくれます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める