printf出力書式まとめ

作成:

変換指定子

出力書式については、かなり前にまとめた事があったが、 (書式付入出力関数(printf 文・scanf 文)の書式) 一覧にするだけでなく、実例を上げながら説明してみようと思う。 もっときちんと網羅されており、手軽に閲覧でき読みやすいドキュメントしては、manが良いと思う。 WebだとMan page of PRINTF を参照すると良い。

printf系の関数のプロトタイプ宣言は以下のようになっている、 ここでいう出力書式というのは以下のchar *fomat引数内に記述する変換書式である。

#include <stdio.h>
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, ...);

過去の記事と同じになってしまうが、変換書式は以下の様な形になっている。 %が現れるとそれ以降に記述される変換指定子にしたがって変換が行われる。 そして、%と変換指定子の間には0個以上の修飾子を挿入できる。

%[フラグ][最小フィールド幅].[精度][長さ修飾子][変換指定子]

先頭から説明し始めると、後ろの説明を見てから出ないとわからないことが多いため、 まずは、変換指定子について説明する。

以下の表は、前述したmanの説明を抜粋しつつ私なりの説明を加えたもの。 詳細についてはmanを参照して欲しい。

変換指定子意味
d, i intを引数として、10進数表記に変換する。
o, u, x, X unsigned intを引数として、符号なしの、8進数(o)、10進数(u)、16進数(x, X)、へ変換する。 xとXの違いは、16進数表記で利用するアルファベットを小文字とするか、大文字とするかの違いである。
e, E doubleを引数として、指定された精度に丸めて、[-]0.000000e±00という形式に変換する。 小数点の前にある数字は必ず1桁で、±のあとの数字は最低2桁、 小数点以下の数字の桁数が精度として指定する値で、デフォルトは6となっている。 学術計算などを行わない場合はあまり見かけない記述かも知れないが、 この表記はソースコード上で浮動小数点リテラルを記述する方法の一つでもある。 eと書かれているとネイピア数(自然対数の底)を彷彿とさせるが、 1.2345e-12であれば\(1.2345 \times 10^{-12}\)を表現している。 eとEの違いは、Eを指定すると、eの代わりにEが用いられるという点だけだ。
f, F doubleを引数として、指定された精度に丸めて、 [-]0000.00という一般的に利用される小数表記に変換する。 小数点以下の桁数が精度として指定する値で、デフォルトは6になっている。
g, G double型を引数として、fもしくはeの形式に変換する。 変換される値の指数が、-4より小さいか、精度以上の場合に、e形式が使用される。 また、eやfと違い、デフォルトの精度は6であるが、小数点以下末尾にある0は省略され、 小数点以下に数字がひとつもない場合は小数点も省略される。
a, A double型を引数として、[-]1.000000p±00という形の16進数を使った表記に変換する。 1.8p+0という表記であれば\(1.8 \times 2^{0}\)という意味で、2進数で1.1、10進数で1.5を表現している。 仮数部の小数点以上の桁は0以外の時必ず1で、指数部分は10進数表記となっている。 浮動小数点数の実際のビット列表現に近い出力を得ることができる。
c int型を引数として、unsinged char型に変換し、対応する文字に変換する。
s const char *型、文字列へのポインタを引数として、指し示す文字列を出力する。 文字列はNULL文字'\0'で終端されている必要がある。
p 引数をvoid *型とみなして、そのポインタの値(アドレス値)を16進数表記で出力する。 32bit環境であれば、ポインタ値をunsigned int値にキャストして%#xに渡した場合と同じになる。 ただし、ポインタ値を整数値にキャストするのは環境依存性が高くなってしまうので、 ポインタ値を適切に出力したい場合は、この変換指定子を利用する。 この変換指定子であれば、32bit環境でも64bit環境でも、同じ記述で適切な変換を行うことができる。
n これまでに出力された文字数を引数のint *型に格納する。出力としては何も行われず、入力動作になる。
m 引数は取らず、strerror(errno)の結果に変換される。glibcの拡張仕様。
% 引数は取らず、一つの%に変換される。

では、具体的にどうすればどうなるのかを、 コード例とその出力という形で示していこうと思う。

intを引数として、10進数変換(d, i)

int x;
x = 10;
printf("%d %i\n", x, x);
x = -10;
printf("%d %i\n", x, x);
10 10
-10 -10

これはなんの変哲もなく、整数を10進数表記に変換するというもの、dでもiでも結果は同じ。 非常によく利用するパターンだと思う、符号も日常的な感覚と同じく、負の場合のみ出力される。

unsigned intを引数として変換(o, u, x, X)

int x;
x = 10;
printf("%o %u %x %X\n", x, x, x, x);
x = -1;
printf("%o %u %x %X\n", x, x, x, x);
12 10 a A
37777777777 4294967295 ffffffff FFFFFFFF

unsigned intを引数として取るものの例。 順に、8進数、10進数、16進数(アルファベット小文字)、16進数(アルファベット大文字)となる。 また、unsigned intを引数として取るので、負の値を引数にすると、 そのビット表現である2の補数をunsigned intとして解釈した値に変換する。 バイナリデータを扱う場合は多用することになるだろう。

doubleを引数として、10進数表記に変換(e, E, f, F, g, G)

double y;
y = 1.0;
printf("%e %E\n", y, y);
printf("%f %F\n", y, y);
printf("%g %G\n\n", y, y);
y = 1.0e-5;
printf("%e %E\n", y, y);
printf("%f %F\n", y, y);
printf("%g %G\n\n", y, y);
y = -1.0e+5;
printf("%e %E\n", y, y);
printf("%f %F\n", y, y);
printf("%g %G\n\n", y, y);
1.000000e+00 1.000000E+00
1.000000 1.000000
1 1

1.000000e-05 1.000000E-05
0.000010 0.000010
1e-05 1E-05

-1.000000e+05 -1.000000E+05
-100000.000000 -100000.000000
-100000 -100000

doubleを引数として取り、10進数表記で変換するものの例。

e, Eの場合は、有効数字を明確にした仮数部+指数部の形式となる。 とは言え、いわゆる演算上の有効数字を配慮してこの変換を行っているわけではなく、 意味のある有効数字を表示したければ、変換書式で精度を明示的に指定する必要がある。

f, Fの場合は、一般的な小数表記になり、精度は小数点以下の桁数を表すことになる。

g, Gの場合は、e形式か、f形式かを出力する値に応じて切り替えて変換してくれる。 単純にe, fどちらかになるだけでなく、小数点以下が0の場合は整数のように表示する。 表示する形式に特に拘らない場合は、g, Gを使うと読みやすい形式に変換してくれる。 符号は負の場合のみ出力される。

また、整数の場合はゼロ除算が発生すると例外が発生するが、 浮動小数点数の場合、NaNやInfという値になる。 この特殊な値も変換可能で、変換させた結果は以下のようになる。

double y;
y = 0./0.;
printf("%e %E\n", y, y);
printf("%f %F\n", y, y);
printf("%g %G\n\n", y, y);
y = 1./0.;
printf("%e %E\n", y, y);
printf("%f %F\n", y, y);
printf("%g %G\n\n", y, y);
-nan -NAN
-nan -NAN
-nan -NAN

inf INF
inf INF
inf INF

一般的な値の場合はfとFに違いはないが、 この場合は出力されるアルファベットの大文字小文字として違いが現れる。

doubleを引数として、16進数表記に変換(a, A)

double y;
y = 1.0;
printf("%a %A\n", y, y);
y = -1.0;
printf("%a %A\n", y, y);
y = 1.0e-1;
printf("%a %A\n", y, y);
y = 1.0e+1;
printf("%a %A\n", y, y);
0x1p+0 0X1P+0
-0x1p+0 -0X1P+0
0x1.999999999999ap-4 0X1.999999999999AP-4
0x1.4p+3 0X1.4P+3

doubleを引数として取り、16進数表記で出力するものの例。 使いどころは正直よく分からないが、 16進数表記なので10進数では丸めが入ってしまう表記も、正確に出力できる。 10進数での0.1が2進数で表現すると循環小数になってしまうのも見て取れる。

文字、及び文字列に変換(c, s)

char*s = "abcd";
printf("%c %s\n", s[0], s);
a abcd

cはint値を引数として、unsigned charにキャストし、対応する文字に変換。

sはconst char *型、つまり、ポインタを引数として、そのポインタが指し示す文字列に変換する。 文字列を扱う場合は常にそうだが、必ずNULL文字'\0'で終端されている必要がある。 もし、NULL文字で終端されていない場合は、NULL文字に出会うまで出力しようとして、 運が良ければ一部のメモリをダンプした出力になり、多くの場合はハングという事態になる。

ポインタ値を16進数に変換(p)

int a;
printf("%p\n", &a);
$ gcc -m32 printf.c
$ ./a.out
0xffcf4bc8

$ gcc printf.c
$ ./a.out
0x7fffe48d3554

ポインタ値を16進数表記に変換する。 サンプルでは、64bit環境を使っているが、ポインタ値のビット数の違いを表現するため、 32ビット向けにコンパイルした結果も合わせて表示している。 ポインタ値のビット幅が変わっても、この方法なら適切に出力してくれる。

その他(n, m, %)

int n;
errno = 0;
printf("%m:%n:%%\n", &n);
printf("%d\n", n);
Success::%
8

最後はこれまでのものとはちょっと毛色が違う。 mはerrnoの表示で紹介したが、 errnoの値を、strerror(errno)とした場合の文字列に変換して出力してくれる。 利用するにあたって、(本当は良くないが)errnoの値を初期化する必要がなければ、errno.hをインクルードする必要もない。 上記例では、errnoの値に特に何も設定されるエラーは発生していないため、Successと変換されている。 ただし、これはCの標準仕様ではなく、glibcの拡張仕様である。

次にnは出力ではなく入力となる、この時点までに変換された文字数を、引数のint*型の指し示す場所に格納する。

最後に%、printfのformat引数では%が現れると、引数を取る変換子として機能してしまうため、 %そのものを最終的に出力したい場合は、%を2つ付けて書くことでひとつの%として変換されるようになっている。 文字列リテラルの中に%が記述できないわけではないので、\%とエスケープしても意味は無い。