printf()関数
フォーマット指定子
これまで、何らかのデータを出力するのに標準で用いてきて
かつC言語を学習するのに、もっとも最初に習う関数こそprintf()関数ですね
しかし、この関数はこれ一つで文字列から数値まで
あらゆるデータを書式化して表示するという、多機能な一面を持っています
今回は、このprintf()関数の知られざる(?)秘密にせまりたいと思います
printf()関数には、ご存知のように多くのフォーマット指定子が存在します
フォーマット指定子は%ではじめ、記号で指定します
代表的なものは、文字出力の%c、符号付10進数の%dなどですね
しかし、以外と使いなれないものもあるかもしれませんので
いま一度、フォーマット指定子の一覧を見てみましょう
#include <stdio.h>
#define STR "Kitty on your lap"
int main() {
int i;
printf("%c : %C - 文字\n" , 'A' , 'A');
printf("%d : %u - 符号付と符号なし10進数\n" , 0xff , 0xff);
printf("%i : %o - 符号付8進数(%dと冗長)と符号なし8進数\n" , 0xff , 0xff);
printf("%x : %X - 符号なし16進数、小文字と大文字\n" , 0xff , 0xff);
printf("%e : %E - [-]d.dddd e [sign]ddd 形式符号付きの値\n" , 1.24 , 1.24);
printf("%f - [-]dddd.dddd 形式の符号付きの値\n" , 1.24);
printf("%g : %G - f,eうち精度を表現できる短い方の書式\n" , 1.24 , 1.24);
printf("%s : %S - ヌル文字までの文字列\n", STR , STR);
printf("Kitty%n <- ここまでの出力数を保存\n" , &i);
printf("%d - ストリームまたはバッファに書き込まれた文字数\n" , i);
printf("%p - ポインタを表示\n" , i);
return 0;
}
文字列までは、説明はいらないと思います
今回はじめて使っているフォーマット指定子が%nと%pです
%nは、この指定子までの文字数を格納します
%nに対応する引数は必ず整数型へのポインタである必要があります
%nフォーマット指定子だけをピックアップして、もう一度見てみましょう
#include <stdio.h>
#define STR "Kitty on your lap"
int main() {
int i , j;
printf("Kitty on your lap%n\n" , &i);
printf("%nKitty on your lap\n" , &j);
printf("i = %d , j = %d" , i , j);
return 0;
}
最初のprintf()では、文字出力後に%nを指定しています
次は、文字を出力する前に%nを指定していますね
実は、後者の場合は出力前のなので変数jには0が入ります
Kitty on your lap
Kitty on your lap
i = 17 , j = 0
このような結果になりました
最初のprintf()関数では、ちゃんと出力数が変数に格納されていますね
文字、文字列、整数、浮動小数点などのデータ型においては、もう説明不要でしょう
さらに、フォーマット指定子の数値データ引数のデフォルトサイズを指定することもできます
これはh , l , Lのいずれかを指定することが可能です
hは short intを(uの場合はshort unsigned int)
l または L は long intを(uの場合はlong unsigned int)を表します
#include <stdio.h>
int main() {
printf("%ld\n" , 0x7fffffff);
printf("%hd\n" , 0x7fffffff);
return 0;
}
コンパイラによっては H を指定しても問題なく動きますが
環境依存になってしまうので、そのようなソースを書くのは控えたほうが良いです
最小フィールド幅とフラグディレクティブ
さらにprintf()関数はフィールド指定や精度指定の指定子が存在します
フォーマット指定子の必項フィールドに対してオプションをつけられるのです
printf()関数の書式指定フィールドは、次の形式になっています
%[フラグ][最小フィールド][ . ][精度][h | l | L]フォーマット指定子
まず最小フィールド幅指定を説明します
最小フィールド幅は負ではない10進整数を指定します
最小フィールドは表示する最小文字数を制御します
ここで指定子多数より表示する文字数が短い場合、デフォルトで右に詰められます
#include <stdio.h>
int main() {
printf("%10d\n" , 10000);
return 0;
}
このプログラムを実行すると、printf()関数が表示するのは5文字です
しかし、最小フィールドが10に指定されているため、残りの5文字も空白として出力されます
結果、出力される文字は5文字だけ右詰めされて表示されます
次に、最初に指定するフラグですが
このフラグディレクティブを用いることで、出力位置を調整することができます
フラグディレクティブは1文字の記号で表します
先程の最小フィールドは場指定は、デフォルトで右詰めでした
そこで、フラグディレクティブで-(マイナス)を指定すると
明示的に左詰に変更することができます
#include <stdio.h>
int main() {
printf("%-10d%d\n" , 10000 , 10);
return 0;
}
左詰にすると、一見普通の出力に見えますが
上のプログラムのように、次の文字を出力すると
ちゃんと残りの5文字が空白で埋められています(つまり、10文字出力されてるということですね)
結果は次のようになります
10000 10
フラグディレクティブに + を指定すると
数値の前に必ず符号が表示されるようになります
デフォルトでは、負数の時のみ - が表示されていましたが
これを指定することで整数のときでも + が数値の前に表示されるようになります
さらにフラグディレクティブは組み合わせて使うこともできます
#include <stdio.h>
int main() {
printf("%\+-10d%+d\n" , 10000 , 10);
return 0;
}
先程のプログラムに + フラグを追加してみました。結果は次のようになります
+10000 +10
見てわかるとおり、他のフラグと組み合わせて使うこともできますし
単体のフラグとして指定することも可能です
さらに、フラグで 0を指定すると最小幅まで0が付加されます
ただしフラグに - が指定されている場合は、それを優先して0は無視されます
また、フラグで空白 ' ' を指定すると
出力値が符号付整数のとき、出力値の前に空白が付きます
フラグで + が指定されている場合は空白フラグは無視されます
#include <stdio.h>
int main() {
printf("%010d\n" , 10000);
printf("%s% d" , "ISO" , 9000);
return 0;
}
次のような結果が得られます
0000010000
ISO 9000
次が最後です。最後のフラグディレクティブは # です
これは、フォーマットがo,x,Xいずれかの場合は、0以外ならば必ず出力値の前に0,0x,0Xをつけます
e,E,f,g,Gの場合は出力値に強制的に小数点を入れます
#include <stdio.h>
int main() {
printf("%#x\n" , 255);
printf("%#e\n" , 0);
printf("%#g" , 0);
return 0;
}
gの場合、後続する0が切り捨てられましたが
#フラグを指定した場合、後続する0は切り捨てられません
精度指定
最後は精度を指定する方法を覚えましょう
精度は最小幅フィールド指定のあとに小数点をつけて指定します
精度はデータ型によって様々な意味を持ちます
たとえば、文字列であれば出力する最大文字数の指定になり
指定数以上の文字数を出力することはできず、あふれた文字は切り捨てられます
整数であれば最小桁数の指定になります
この場合、最小桁数以上の桁でも切り捨てられることはありません
最小桁数以下の場合は、上位に0が付加されます
浮動少数のeやfであれば小数点以下の表示桁数の指定になり
gまたはGであれば最大有効桁数の指定(つまり全体)になります
#include <stdio.h>
int main() {
printf("%5.3s\n" , "Kitty");
printf("%5.3d\n" , 1);
printf("%.3e\n" , 10.10);
printf("%.3f\n" , 10.10);
printf("%.3g\n" , 10.10);
return 0;
}
次のような結果が得られます
Kit
001
1.010e+01
10.100
10.1
精度指定はc,Cには無効です
当然%%や%pなどにも意味がありません
書式指定フィールドの機能を乱用すると、見ずらいソースになりがちです
必要以上に乱用しないようにしましょう