feature image

2020年5月18日 | ブログ記事

printfを飼いならす(初級編)【新歓ブログリレー71日目】

この記事は新歓ブログリレー71日目の記事です。

sea314です。

今回はC言語標準出力関数として超有名なprintf関数を2回に分けてじっくり解説していきます。


printfとは?

#include <stdio.h>

int main(){
	int num = 3;
	double pi = 3.141592;
	char *str = "円周率";
	printf("num:%d\npi:%f\nstr:%s", num, pi, str);
	return 0;
}

intやdouble、charといったあらゆる変数を与えられた書式に従って文字列として画面に出力する便利な関数です。C言語専用の関数ですが、C#、Java、Ruby、MATLABなど幅広い言語で同等の機能が実装されており、その一部は同じprintfという名前で移植されています。

読み方についてですが、一般にはプリントエフと読むようです。Print Formatの略なので私はプリントフと呼んでいます。

ところで、上に書いたサンプルプログラム、printfでどのように出力されるか分かりますか?%d、%f、%sのところがそれぞれ3、3.141592、円周率に置き換わるので、

num:3
pi:3.141592
str:円周率

のように出力されます。%fに関しては環境によっては3.142のように途中で切れたり、3.14159200のように余計に表示されたりします。

書式

変換指定子

%d、%fなどのdやfの部分を変換指定子と呼びます。C言語はかなり古い言語のため、printf関数は変数numを渡されてもint型なのかdouble型なのか判別する術を持ちません。したがってprintfを使うプログラマーが教えてやる必要があります。また、浮動小数点数型などでは12.345の通常の小数表現と1.2345E+01の指数表現とどちらで表示するべきかなども指定できます。

指定子 変数 説明 備考
d,i 整数型 符号あり10進整数に変換します。
u 整数型 符号なし10進整数に変換します。
o 整数型 符号なし8進整数に変換します。
x 整数型 符号なし16進整数に変換します。
f 浮動小数点数型 通常の小数表現に変換します。
e,E 浮動小数点数型 指数表現に変換します。 ※1
g,G 浮動小数点数型 通常の小数表現と指数表現の適切な方に変換します。 ※1
c char(,int) 1文字を表す値と見なし文字に変換します。 ※2
s char* \0(ヌル文字)で終わる文字列の先頭アドレスと見なし文字列に変換します。 ※3
p ポインタ型 ポインタ型の指すアドレスを見やすい形に変換します。
% %記号そのものを表示します。 ※4

※1 小文字の方を使うと1.2345e+01、大文字の方を使うと1.2345E+01のようにeの大小が変わります。

※2 intより小さい整数型はprintfに引数で渡す際にint型に拡張されるので、intもこの指定子を使うことが出来ます。getc関数などは文字を返しますがエラー(-1)を示すために戻り値がint型になっています。

※3 C言語には文字列型というものが存在せず、文字を格納するのに使うchar型を配列にして使います。それだと文字列の長さがわからないので配列の末尾にヌル文字を入れて文字列として扱います。

※4 つまり%%と書くことで%が表示されます。

変換修飾子

変換指定子だけでは変数のサイズがわからないので、必要に応じて変換修飾子を変換指定子の直前に付ける必要があります。整数型はint、浮動小数点数型はdoubleが基準なのでこの2つには変換修飾子が不要です。floatもprintfには引数で渡す際にdouble型に拡張されるので変換修飾子が不要です。

余談ですが、引数に渡された変数のサイズすらわからないのはC言語くらいだと思うので、他言語でのprintfにはこの変換修飾子が要らないと思います。

修飾子 変数 説明 備考
h short 整数型がshortであることを示します。 ※1
l long 整数型がlongであることを示します。
ll long long 整数型がlong longであることを示します。
L long double 浮動小数点数型がlong doubleであることを示します。

※1 intより小さい整数型はprintfに引数で渡す際にint型に拡張されるので、この変換修飾子は省略できます。

scanfの変換修飾子と変換指定子と間違えて、printfでdoubleに%lfとlを付ける人がいますが間違いです。あまりに間違える人が多かったため、%lfと書いても動くように改定されたようですが。scanfではfloatのdoubleへの型拡張の恩恵を受けることが出来ないので、floatは%f、doubleは%lfとなります。

一旦休憩

ここまでのことは他の言語なら比較的簡単にできると思いますが、printfは説明したとおり結構面倒くさいです。変換修飾子や変換指定子は間違えると不正にメモリにアクセスして異常終了することがあり間違えると危険です。ではなぜその面倒とリスクをとってまでprintfを使うのか。それはこのあと説明する書式がかなり便利だからだと思っています。

書式(続き)

printfの書式は実はかなり複雑です。

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

[]で書かれている部分は省略可能です。

フラグ

書式に関する設定をします。

フラグ 説明 備考
- 左詰めで表示します。 省略時は右詰めです。
+ 正の数の時にも+の符号を表示します。
# %xのときは0xを,%oのときは0を先頭に追加します。
0 整数の先頭に0を必要数追加します。 フィールド幅と組み合わせて使います。

最小フィールド幅

出力する幅を指定します。足りない幅はフラグに応じて半角スペースか0を追加します。

printf("%5d\n",3);
printf("%5s\n", "abcdefg");
printf("%5f", 3.14);

のように書くと

    3
abcdefg
3.140000

のように出力されます。

フィールド幅に*を指定することで、幅を引数で指定できます。

printf("%*d\n", 5, 3);
printf("%*s\n", 5, "abcdefg");
printf("%*f", 5, 3.14);

精度

出力する精度を桁数で指定します。整数の場合は足りない場合は0埋め、余る場合はそのまま出力されます。小数は小数点以下の桁数になります。文字列は指定した長さだけ出力されます。

printf("%.4d\n", 3);
printf("%.3s\n", "abcdefg");
printf("%.1f", 3.14);

出力

0003
abc
3.1

最小フィールド幅同様、精度に*を指定することで精度を引数で指定できます。

printf("%.*d\n", 4 ,3);
printf("%.*s\n", 3 ,"abcdefg");
printf("%.*f", 1 ,3.14);

練習問題

問題

次のプログラムはなんと表示されるでしょうか

問1 整数

printf("%+.*d\n", 7, 12345);
printf("%7d\n", -12345);
printf("%07d\n", -12345);
printf("%#x\n", 12345);

問2 小数

printf("%012.3f\n", -12.3456);
printf("%*.*f\n", 12, 3, -12.3456);
printf("%12.3e\n", -12.3456);

問3 文字列

printf("%.7s\n", "abcde");
printf("%*s\n", 3, "abcde");
printf("%*.*s\n", 7, 3, "abcde");

解答

問1

+0012345	// 符号表示、桁数7
 -12345		// 最小幅7
-012345		// 0埋め、最小幅7
0x3039		// 0x表示、16進表示

問2

-0000012.346	// 0埋め、最小幅12、小数点以下3桁
     -12.346	// 最小幅12、小数点以下3桁
  -1.235e+01	// 最小幅12、小数点以下3桁、指数表示

問3

abcde			// 文字数7
abcde			// 最小幅3
    abc			// 最小幅7、文字数3

最後に

今日の記事ではprintfのフォーマットについて説明しましたが、この程度の情報は少し調べたら山ほど出てくると思います。明日の記事はprintfを応用した内容について書きたいと思います。

明日のブログリレーはFacishさんの記事と、この記事の上級編です。お楽しみに!

sea314 icon
この記事を書いた人
sea314

Monsterのエディタ&ゲーム本体のファイル入出力系を担当しています。 C/C++を主に使っています。 「しーさんいちよん」と読みます

この記事をシェア

このエントリーをはてなブックマークに追加
共有
記事一覧 タグ一覧 Google アナリティクスについて