C言語の基本|printfの書式指定

同じ値でも、書式を変えれば見え方が変わる。printfの書式指定を覚えて、数値を目的に合った形で表示しよう。

printfは、文字列を画面に表示するだけの関数ではありません。変数に保存されている整数や文字などを、文章の中に組み込んで表示することもできます。

そのときに使うのが、書式指定です。

たとえば、int型の変数scoreに85が入っているとします。

int score = 85;

この値を文章の中に表示する場合は、次のように%dを使います。

printf("テストの点数は%d点です\n", score);

実行結果は次のようになります。

テストの点数は85点です

書式文字列にある%dが値を表示する場所になり、その後ろに指定したscoreの値が当てはめられています。

さらに、整数は10進数だけでなく、8進数や16進数でも表示できます。同じ値を異なる進数で確認できるため、進数の学習やプログラムの動作確認にも役立ちます。

ここでは、整数の表示に使う%d、%u、%o、%x、%Xを中心に、書式指定と値の対応、複数の値を表示する方法、間違えやすいポイントを順番に確認していきましょう。

printfは値を文章の中に組み込める

文字列だけを表示するprintfは、次のように記述します。

printf("C言語を学習します\n");

この場合、ダブルクォーテーションで囲まれた文字列が、そのまま画面に表示されます。

C言語を学習します

変数や数値を文章の中に組み込みたい場合は、書式文字列に書式指定を書き、その後ろに表示したい値を渡します。

printf("練習問題は%d問です\n", 12);

実行結果は次のとおりです。

練習問題は12問です

このprintfには、2つの引数があります。

引数の位置指定した内容役割
1つ目書式文字列表示する文章と値の形式を指定する
2つ目12%dの位置に表示する整数

書式文字列は、通常の文字、書式指定、エスケープシーケンスなどを組み合わせて作れます。

printf("練習問題は%d問です\n", 12);

この書式文字列を分けると、次のようになります。

部分意味
練習問題はそのまま表示する文字
%d整数を10進数で表示する場所
問ですそのまま表示する文字
\n表示後に改行する

書式指定の基本的な形

書式指定は、パーセント記号と変換指定子を組み合わせて記述します。

% + 変換指定子

整数を10進数で表示する%dでは、次のように分けられます。

% + d

入門段階では、%d全体を整数用の書式指定として覚えておけば大丈夫です。

printfの基本的な形は次のとおりです。

printf("書式文字列", 表示する値);

変数を使う場合は、次のように書きます。

printf("点数は%d点です\n", score);

このコードでは、printfが書式文字列を左から読み、%dの位置にscoreの値を表示します。

図1:書式指定の位置に変数の値が入る仕組み

この図から分かること

printfでは、最初の引数に表示方法を表す書式文字列を指定し、2つ目以降の引数に実際の値を渡します。

%dは整数を表示する場所です。printfは%dを見つけると、対応する引数の値を10進数に変換して表示します。

整数で使う主な書式指定

整数を表示するときは、目的に応じて書式指定を使い分けます。

書式指定対応する型表示方法値が42の場合
%dint符号付き10進数42
%uunsigned int符号なし10進数42
%ounsigned int8進数52
%xunsigned int16進数の英字を小文字で表示2a
%Xunsigned int16進数の英字を大文字で表示2A

普段使っている10進数としてint型の値を表示する場合は、%dを使用します。

int score = 85;

printf("%d\n", score);

8進数や16進数の表示には、%o、%x、%Xを使用します。これらはunsigned int型に対応するため、進数を比較するサンプルではunsigned int型の変数を使うと、型と書式指定の関係が明確になります。

unsigned int number = 42;

printf("%u\n", number);
printf("%o\n", number);
printf("%x\n", number);
printf("%X\n", number);

%dで符号付き10進数を表示する

%dは、int型の整数を符号付き10進数で表示します。

int temperature = -5;

printf("気温は%d度です\n", temperature);

実行結果は次のようになります。

気温は-5度です

符号付き整数なので、正の数、0、負の数を表示できます。

printf("%d\n", 25);
printf("%d\n", 0);
printf("%d\n", -8);

25
0
-8

%dは、個数、点数、温度など、通常の整数表示でよく使用します。

%uで符号なし10進数を表示する

%uは、unsigned int型の値を10進数で表示します。

unsigned int item_count = 25;

printf("商品数は%u個です\n", item_count);

実行結果は次のとおりです。

商品数は25個です

unsigned int型は、負の値を扱わない整数型です。個数や識別番号など、0以上の整数を扱う場面で使われます。

%u、%o、%x、%Xは、いずれもunsigned int型の値を異なる形式で表示する書式指定です。

%oで8進数を表示する

%oは、unsigned int型の値を8進数に変換して表示します。

unsigned int number = 42;

printf("%o\n", number);

実行結果は次のようになります。

52

これは、変数numberの値が52に変わったわけではありません。内部で保持している値は42のままで、printfが8進数の52という文字の並びに変換して表示しています。

42(10進数)=52(8進数)

%xで小文字の16進数を表示する

%xは、unsigned int型の値を16進数で表示します。10以上の桁には、小文字のaからfが使われます。

unsigned int number = 42;

printf("%x\n", number);

実行結果は次のようになります。

2a

16進数のaは、10進数の10を表します。

42(10進数)=2a(16進数)

%xだけでは、表示結果の先頭に0xは付きません。値42を%xで表示した結果は2aです。

%Xで大文字の16進数を表示する

%Xも16進数の表示に使いますが、10以上の桁を大文字のAからFで表示します。

unsigned int number = 42;

printf("%X\n", number);

実行結果は次のとおりです。

2A

%xと%Xの違いは、16進数に含まれる英字の大文字と小文字です。

書式指定英字の表示値42の表示結果
%xaからf2a
%XAからF2A

値そのものに違いはありません。資料やプログラム全体の表記方針に合わせて使い分けます。

同じ値でも表示方法を変えられる

整数の値は、書式指定を変えることで、10進数、8進数、16進数として表示できます。

42(10進数)
52(8進数)
2a(16進数・小文字)
2A(16進数・大文字)

これは4つの異なる値ではありません。すべて同じ数量を、異なる進数で表しています。

身近な例で考えると、1メートルと100センチメートルが同じ長さを表すことに少し似ています。表し方は異なっても、元になる量は変わりません。

図2:同じ整数を10進数・8進数・16進数で表示する

この図から分かること

変数numberには、1つの整数値42が保存されています。printfに渡すときに書式指定を変えることで、10進数の42、8進数の52、16進数の2aまたは2Aとして表示できます。

書式指定が変えているのは画面上の見せ方です。変数に保存されている値そのものを書き換えているわけではありません。

複数の書式で整数を表示してみよう

同じ整数を複数の進数で表示するプログラムを作成します。変数の値は、参考例とは異なる42を使用します。

同じ整数を4種類の書式で表示するプログラム

ファイル名:5_3_1.c

#include <stdio.h>

int main(void)
{
    /* 進数を変えて表示する整数 */
    unsigned int number = 42;

    /* 同じ値を10進数、8進数、16進数で表示する */
    printf("10進数で表示      : %u\n", number);
    printf("8進数で表示       : %o\n", number);
    printf("16進数の小文字表示: %x\n", number);
    printf("16進数の大文字表示: %X\n", number);

    /* 複数の書式指定を1つのprintfで使用する */
    printf("一覧 → 10進:%u\t8進:%o\t16進:%x\n",
           number, number, number);

    return 0;
}
実行結果の例
10進数で表示      : 42
8進数で表示       : 52
16進数の小文字表示: 2a
16進数の大文字表示: 2A
一覧 → 10進:42    8進:52    16進:2a

変数numberをunsigned int型で宣言している

表示する値は、unsigned int型の変数numberに保存しています。

unsigned int number = 42;

unsigned int型は、0以上の整数を扱う型です。

今回使う%u、%o、%x、%Xはunsigned int型に対応しているため、変数の型と書式指定が一致しています。

書式指定だけを変えている

次の4つのprintfには、すべて同じnumberを渡しています。

printf("10進数で表示      : %u\n", number);
printf("8進数で表示       : %o\n", number);
printf("16進数の小文字表示: %x\n", number);
printf("16進数の大文字表示: %X\n", number);

異なっているのは書式指定です。

printfで使う指定numberの表示結果
%u42
%o52
%x2a
%X2A

変数numberを何度も初期化したり、別の値を代入したりしているわけではありません。printfが指定に合わせて、文字としての表現を作っています。

1つのprintfに複数の値を渡せる

最後のprintfには、3つの書式指定があります。

printf("一覧 → 10進:%u\t8進:%o\t16進:%x\n",
       number, number, number);

書式指定は、左から次の順番で並んでいます。

  1. %u
  2. %o
  3. %x

その後ろには、numberを3回渡しています。

書式指定の順番書式指定対応する引数表示結果
1%u1つ目のnumber42
2%o2つ目のnumber52
3%x3つ目のnumber2a

printfは、書式文字列の変換指定と、その後ろに並ぶ値を左から順番に対応させます。

\tと組み合わせて横に並べる

最後のprintfでは、エスケープシーケンスの\tも使用しています。

printf("一覧 → 10進:%u\t8進:%o\t16進:%x\n",
       number, number, number);

\tによって、それぞれの表示項目の間にタブを入れています。

一覧 → 10進:42    8進:52    16進:2a

タブの幅は実行環境や現在の表示位置によって変わるため、必ず同じ数の空白が入るわけではありません。ただし、簡単な一覧を横に並べる場合には便利です。

書式指定と値の数をそろえよう

printfでは、書式文字列に含まれる書式指定の数と、後ろに渡す値の数をそろえる必要があります。

次のコードでは、%dが2つあり、整数も2つ渡しています。

printf("%d %d\n", 10, 20);

対応関係は次のとおりです。

1つ目の%d ← 10
2つ目の%d ← 20

実行結果は次のようになります。

10 20

一方、次のコードでは、%dが2つあるのに値が1つしかありません。

printf("%d %d\n", 10);

2つ目の%dに対応する値がないため、動作は正しく保証されません。たまたま何らかの値が表示されても、その結果を利用してはいけません。

反対に、書式指定より多くの値を渡すのも正しい書き方ではありません。

printf("%d\n", 10, 20);

この場合、20に対応する書式指定がありません。コンパイラの警告を有効にしていると、こうした間違いを見つけやすくなります。

型に合った書式指定を使用する

書式指定には、対応するデータ型があります。

printf("%d\n", 10, 20);

この組み合わせでは、int型と%dが対応しています。

unsigned int number = 42;
printf("%x\n", number);

こちらは、unsigned int型と%xが対応しています。

次のように、double型の値を%dで表示することはできません。

double rate = 3.5;
printf("%d\n", rate);

double型の値を通常の小数として表示する場合は、%fを使います。

double rate = 3.5;
printf("%f\n", rate);

文字列には%s、1文字には%cを使用します。

データ型やデータの種類書式指定の例
int%d
unsigned int%u、%o、%x、%X
double%f
char%c
文字列%s

型と書式指定が合っていない場合、単に表示が少し変わるだけではありません。正しい動作が保証されないため、変数の型を確認してから書式指定を選びましょう。

図3:複数の書式指定と引数が左から対応する

この図から分かること

書式文字列に複数の書式指定がある場合、その後ろに渡した値が左から順番に対応します。

書式指定が3つなら、基本的には対応する値も3つ必要です。また、個数をそろえるだけでなく、各値の型と書式指定を合わせることも大切です。

値の順番にも注意しよう

書式指定と値の数が合っていても、値の順番を間違えると、文章の意味が変わってしまいます。

int item_count = 3;
int unit_price = 200;

printf("個数は%d個、単価は%d円です\n",
       item_count, unit_price);

実行結果は次のようになります。

個数は3個、単価は200円です

値を逆に渡すと、次のようになります。

printf("個数は%d個、単価は%d円です\n",
       unit_price, item_count);

個数は200個、単価は3円です

どちらの値もint型なので、型の違いによる警告は出ない可能性があります。しかし、表示内容は意図したものではありません。

複数の値を渡すときは、次の2点を確認しましょう。

  • 書式指定の数と値の数が一致しているか
  • 書式指定と値が正しい順番で対応しているか

%x と %X を使い分けよう

%xと%Xは、どちらも16進数を表示します。違いは、10から15を表す英字の大文字と小文字です。

unsigned int code = 47;

printf("小文字: %x\n", code);
printf("大文字: %X\n", code);

実行結果は次のとおりです。

小文字: 2f
大文字: 2F

表示する資料やプログラムの方針に合わせて、どちらかに統一すると読みやすくなります。

たとえば、次のような使い分けが考えられます。

  • 小文字で統一したい場合は%x
  • 大文字で強調したい場合は%X
  • 既存の表示形式に合わせる
  • プロジェクトの命名・表示ルールに合わせる

パーセント記号を表示する%%

書式文字列内のパーセント記号は、書式指定の開始として扱われます。

パーセント記号そのものを表示したい場合は、%%と書きます。

printf("進捗率は80%%です\n");

実行結果は次のようになります。

進捗率は80%です

同じように、%dという文字を変換指定としてではなく、そのまま画面に表示したい場合は、%を%%にします。

printf("整数の書式指定は%%dです\n");

実行結果は次のとおりです。

整数の書式指定は%dです

%%は値を受け取る書式指定ではありません。そのため、%%に対応する引数を後ろへ追加する必要はありません。

printf("達成率は100%%です\n");

表示結果が変わっても値は変化しない

次のコードでは、同じnumberを4回表示しています。

unsigned int number = 42;

printf("%u\n", number);
printf("%o\n", number);
printf("%x\n", number);
printf("%X\n", number);

画面には42、52、2a、2Aと表示されますが、numberの中身が順番に書き換えられているわけではありません。

printfは、値を指定された形式の文字列へ変換し、それを標準出力へ送っています。変数そのものへの代入は行っていないため、表示後もnumberの値は42のままです。

これは、進数表示を理解するときに特に大切な考え方です。

変数の値 42
    ↓
printfの書式指定で見せ方を選ぶ
    ↓
42、52、2a、2Aとして表示

実際に書式指定を使ってみよう

次の実行結果になるように、unsigned int型の変数data_codeを45で初期化し、10進数、8進数、16進数で表示してみましょう。

10進数 : 45
8進数  : 55
16進数 : 2d
10進数:45    8進数:55    16進数:2d

使用する書式指定は次のとおりです。

  • 10進数には%u
  • 8進数には%o
  • 16進数には%x
整数45を3種類の進数で表示するプログラム

ファイル名:5_3_2.c

#include <stdio.h>

int main(void)
{
    /* 表示する値をunsigned int型の変数に保存する */
    unsigned int data_code = 45;

    /* 10進数、8進数、16進数で個別に表示する */
    printf("10進数 : %u\n", data_code);
    printf("8進数  : %o\n", data_code);
    printf("16進数 : %x\n", data_code);

    /* 3種類の表示を1行にまとめる */
    printf("10進数:%u\t8進数:%o\t16進数:%x\n",
           data_code, data_code, data_code);

    return 0;
}
実行結果の例
10進数 : 45
8進数  : 55
16進数 : 2d
10進数:45    8進数:55    16進数:2d

data_codeの値と型を確認しよう

変数data_codeは、unsigned int型として宣言し、45で初期化しています。

unsigned int data_code = 45;

0以上の整数として扱い、%u、%o、%xで表示するため、unsigned int型を使っています。

書式指定を変えても、data_codeに保存されている値は45のままです。

45を8進数で表示すると55になる

%oを使うと、45が8進数に変換されて表示されます。

printf("8進数  : %o\n", data_code);

8進数の55を10進数として考えると、次の計算になります。

5×81+5×80
=40+5
=45

そのため、10進数の45と8進数の55は同じ値を表します。

45を16進数で表示すると2dになる

%xを使うと、45が16進数の小文字表記で表示されます。

printf("16進数 : %x\n", data_code);

16進数のdは、10進数の13を表します。

2×161+13×160
=32+13
=45

したがって、10進数の45と16進数の2dは同じ値です。

大文字で2Dと表示したい場合は、%xを%Xに変更します。

printf("16進数 : %X\n", data_code);

最後のprintfでは値を3回渡している

最後のprintfには、%u、%o、%xの3つの書式指定があります。

printf("10進数:%u\t8進数:%o\t16進数:%x\n",
       data_code, data_code, data_code);

そのため、後ろにもdata_codeを3回指定しています。

%u ← 1つ目のdata_code
%o ← 2つ目のdata_code
%x ← 3つ目のdata_code

同じ変数でも、必要な書式指定ごとに引数として渡す必要があります。

書式指定の練習に向いている値

進数による表示の違いを確認するときは、次のような値を試すと変化が分かりやすくなります。

10進数8進数16進数
1012a
1517f
162010
31371f
42522a
45552d
255377ff

プログラム内の変数を変更するだけで、各進数の表示を確認できます。

unsigned int number = 255;

この値を%u、%o、%xで表示すると、次の結果になります。

10進数:255
8進数:377
16進数:ff
10進数:255
8進数:377
16進数:ff

まずは小さな整数を使い、同じ値が書式指定によってどのように見え方を変えるのか試してみてください。%dや%uによる10進数表示が理解できれば、%oによる8進数、%xと%Xによる16進数も、同じ仕組みとして読み取れるようになります。