C言語での画面出力はほぼすべてprintfから始まると言ってもよいほど重要です。
しかし、書式指定子(フォーマット指定)が分かりにくく、なんとなく使っている方も多いと思います。
この記事では「これだけ覚えれば実務で困らない」printfの書式指定子を一覧と実例付きで丁寧に解説します。
C言語のprintfとは
printfの基本構文と使い方

C言語のprintfは、標準ライブラリstdio.hに定義されている画面出力専用の関数です。
最も基本的な構文は次の形になります。
#include <stdio.h>
int main(void) {
int score = 90;
// 書式文字列と値を組み合わせて出力
printf("合計 = %d 点\n", score);
// ^^^^^^^^ 書式文字列
// ^^^^^ 引数(値)
return 0;
}
このときの構文は次のように整理できます。
- 最初の引数が書式文字列(format string)
- 書式文字列の中に書式指定子(例:
%d)を書く - 2番目以降の引数で、その
%dなどに対応する値を渡す
書式指定子と引数の型が対応していることが非常に重要です。
たとえば%dならint、%fならdoubleを渡す必要があります。
標準出力とフォーマット指定の役割

C言語には標準出力(standard output)という概念があります。
通常はコンソール(ターミナル)画面に接続されていて、printfはこの標準出力へ文字を送り出します。
フォーマット指定(書式指定子)には次のような役割があります。
- 数値を10進数/16進数/8進数などで表示する
- 桁数や小数点以下の桁数をそろえる
- 左寄せ/右寄せ、ゼロ埋めなど見た目を整える
- 文字列を一部だけ抜き出して表示する
「値」そのものは数値やポインタですが、printfはそれを人間が読みやすい文字列に変換する関数だと考えると理解しやすくなります。
printf書式指定子一覧
ここでは、まずよく使う書式指定子を一覧で整理します。
そのあと各項目を詳しく見ていきます。

代表的な書式指定子を一覧表にまとめます。
| 書式指定子 | 意味 | 対応する型(基本) |
|---|---|---|
%d, %i | 符号付き10進整数 | int |
%u | 符号なし10進整数 | unsigned int |
%x, %X | 16進整数(小文字/大文字) | unsigned int |
%o | 8進整数 | unsigned int |
%c | 1文字 | int(char昇格) |
%s | 文字列 | char * |
%f | 実数(小数点) | double |
%e, %E | 指数表記の実数 | double |
%p | ポインタ(アドレス) | void *など |
%n | ここまでの文字数を格納 | int *など |
このほかにも%gや%aなどがありますが、まずは上の一覧を押さえれば一般的な開発で十分対応できます。
整数用書式指定子%dと%i
%dと%iは符号付き10進整数を表示します。
実務では%dを使うことがほとんどです。
#include <stdio.h>
int main(void) {
int a = 10;
int b = -20;
printf("a = %d\n", a); // 10進数で表示
printf("b = %d\n", b); // 符号付きで表示
// %i も %d とほぼ同じように動く
printf("a (%%i) = %i\n", a);
printf("b (%%i) = %i\n", b);
return 0;
}
a = 10
b = -20
a (%i) = 10
b (%i) = -20
printfの中では%cst-code>%dと%iはほぼ同義と覚えておけば十分です。
符号なし整数用書式指定子%u
%uは符号なし(unsigned)の10進整数を表示します。
#include <stdio.h>
int main(void) {
unsigned int u = 4000000000U;
int s = -1;
printf("unsigned = %u\n", u);
// 故意に型を揃えない例(本来はNG)
printf("signed(-1)を %%u で表示すると: %u\n", s);
return 0;
}
unsigned = 4000000000
signed(-1)を %u で表示すると: 4294967295
変数の型と書式指定子が一致していないと意味不明な値になるので、特に符号付きか符号なしかは常に意識する必要があります。
16進数・8進数表示の%xと%o
%xと%oは、デバッグやビット操作の確認で頻出です。
#include <stdio.h>
int main(void) {
unsigned int v = 255;
printf("10進: %u\n", v);
printf("16進(小文字): %x\n", v);
printf("16進(大文字): %X\n", v);
printf("8進: %o\n", v);
return 0;
}
10進: 255
16進(小文字): ff
16進(大文字): FF
8進: 377
ビット列を確認するときは16進数で見るほうが直感的な場合が多いため、%xは特に重要です。
文字・文字列表示の%cと%s
文字と文字列はよく似ていますが、型と意味がまったく違う点に注意が必要です。
#include <stdio.h>
int main(void) {
char ch = 'A'; // 1文字
char str[] = "Hello"; // 文字列(終端に '\0')
printf("文字: %c\n", ch);
printf("文字列: %s\n", str);
// %c には int(実際はcharがintに昇格したもの)を渡す
// %s には char * (文字列の先頭アドレス)を渡す
return 0;
}
文字: A
文字列: Hello
文字列リテラル”Hello”はchar *として扱われることも合わせて意識しておきましょう。
実数表示の%fと%e・%E
%fは小数点付きの通常表記、%eと%Eは指数表記で実数を表示します。
いずれもdoubleが対象です。
#include <stdio.h>
int main(void) {
double x = 12345.6789;
printf("通常表記 %%f: %f\n", x);
printf("指数表記 %%e: %e\n", x);
printf("指数表記 %%E: %E\n", x);
return 0;
}
通常表記 %f: 12345.678900
指数表記 %e: 1.234568e+04
指数表記 %E: 1.234568E+04
既定では小数点以下6桁まで表示されます。
桁数をそろえたい場合は後述の%.2fのような精度指定を使います。
ポインタ表示の%p
%pはポインタのアドレス値を表示します。
デバッグ時に非常によく使います。
#include <stdio.h>
int main(void) {
int value = 100;
int *p = &value;
printf("value のアドレス: %p\n", (void *)&value);
printf("p の値(同じアドレス): %p\n", (void *)p);
return 0;
}
value のアドレス: 0x7ffeefbff45c
p の値(同じアドレス): 0x7ffeefbff45c
%p ではvoid *にキャストして出力するのが標準的とされています。
printf書式指定子の詳細と応用
ここからは、書式指定子の「オプション部分」(フィールド幅、フラグ、精度など)について詳しく説明します。
書式指定子の一般形は次のようになります。
%[フラグ][フィールド幅][.精度][サイズ修飾子]変換指定子
フィールド幅と左寄せ・右寄せ

フィールド幅を指定すると、「その数だけの桁を使って表示する」ことができます。
足りない部分は空白で埋められます。
#include <stdio.h>
int main(void) {
int n = 42;
printf("右寄せ 幅5: '%5d'\n", n);
printf("左寄せ 幅5: '%-5d'\n", n);
return 0;
}
右寄せ 幅5: ' 42'
左寄せ 幅5: '42 '
%5d: 幅5、右寄せ(デフォルト)%-5d: 幅5、左寄せ(-フラグ)
列をそろえたいときに非常に便利です。
ゼロ埋めと符号表示

0フラグをつけると、空白ではなく0で埋めることができます。
また+フラグでプラスの数にも+を表示できます。
#include <stdio.h>
int main(void) {
int a = 42;
int b = -42;
printf("通常 : '%5d' '%5d'\n", a, b);
printf("ゼロ埋め : '%05d' '%05d'\n", a, b);
printf("符号表示 : '%+5d' '%+5d'\n", a, b);
return 0;
}
通常 : ' 42' ' -42'
ゼロ埋め : '00042' '-0042'
符号表示 : ' +42' ' -42'
ゼロ埋めは符号の右側から埋まるため、負の値でも-0042のような表示になります。
精度指定(%.2fや%.3s)の使い方
精度指定は小数点以下の桁数や、文字列の最大文字数などを制御します。
#include <stdio.h>
int main(void) {
double pi = 3.1415926535;
printf("デフォルト : %f\n", pi);
printf("小数2桁 : %.2f\n", pi);
printf("小数4桁 : %.4f\n", pi);
printf("幅8, 小数2桁: %8.2f\n", pi);
return 0;
}
デフォルト : 3.141593
小数2桁 : 3.14
小数4桁 : 3.1416
幅8, 小数2桁: 3.14
- 実数:
%.2fなら小数点以下2桁に丸めて表示 - 文字列:
%.3sなら最大3文字までを表示
文字列の切り出し表示(%.Ns)の実例

%.Nsを使うと先頭から最大N文字だけを表示できます。
ログ出力やテーブル出力で、長すぎる文字列を切りたいときに便利です。
#include <stdio.h>
int main(void) {
char *s = "HelloWorld";
printf("元の文字列 : '%s'\n", s);
printf("先頭5文字だけ : '%.5s'\n", s);
printf("先頭8文字だけ : '%.8s'\n", s);
printf("幅10, 先頭5文字 : '%10.5s'\n", s);
printf("左寄せ, 幅10,5文字: '%-10.5s'\n", s);
return 0;
}
元の文字列 : 'HelloWorld'
先頭5文字だけ : 'Hello'
先頭8文字だけ : 'HelloWor'
幅10, 先頭5文字 : ' Hello'
左寄せ, 幅10,5文字: 'Hello '
幅と精度を組み合わせることで「切り出しつつ列をそろえる」ことができます。
フラグ(#, 0, +, 空白, -)の意味と使い分け
代表的なフラグを整理しておきます。
| フラグ | 意味の概要 | 例 |
|---|---|---|
- | 左寄せ | %-5d |
+ | 正数にも+を付ける | %+d |
| (空白) | 正数のとき先頭に空白1つ(負数の-と幅を合わせる) | % d |
0 | ゼロ埋め | %05d |
# | 進数表記にプレフィックスや小数点を付ける | %#x, %#o, %#f |
具体例を見てみましょう。
#include <stdio.h>
int main(void) {
int n = 255;
printf("通常16進 : %x\n", n);
printf("#付き16進 : %#x\n", n);
printf("通常8進 : %o\n", n);
printf("#付き8進 : %#o\n", n);
double x = 10.0;
printf("通常%%g : %g\n", x);
printf("#付き%%g : %#g\n", x);
return 0;
}
通常16進 : ff
#付き16進 : 0xff
通常8進 : 377
#付き8進 : 0377
通常%g : 10
#付き%g : 10.0000
#フラグで「16進なら0x, 8進なら0を付ける」など、形式が明確になるため、デバッグ出力でよく使われます。
longやshortなどサイズ修飾子

整数型のサイズによってサイズ修飾子を付けます。
環境によって型サイズは異なりますが、書式と対応は次のようになります。
| 修飾子 | 例 | 対応する型の例 |
|---|---|---|
| なし | %d | int |
h | %hd | short |
hh | %hhd | signed char |
l | %ld | long |
ll | %lld | long long |
z | %zu | size_t |
t | %td | ptrdiff_t |
実例として、long longの値を表示してみます。
#include <stdio.h>
int main(void) {
long long big = 123456789012345LL;
printf("long long: %lld\n", big);
return 0;
}
long long: 123456789012345
型に合った修飾子を使わないと未定義動作になるので、表と一緒に覚えておくと安全です。
printfでよくあるエラーと注意点

printfでよく起こる問題をまとめておきます。
1つ目は書式指定子と引数の型不一致です。
#include <stdio.h>
int main(void) {
double x = 3.14;
// 間違い: %d は int 用なのに double を渡している
printf("値 = %d\n", x); // 未定義動作、警告が出るはず
return 0;
}
コンパイラの警告を必ず確認し、警告は0件を維持することが重要です。
2つ目は引数の個数が合わないパターンです。
#include <stdio.h>
int main(void) {
int a = 10, b = 20;
// NG: 書式文字列に2つの%dがあるのに、引数が1つしかない
printf("a=%d, b=%d\n", a); // 未定義動作
// OK
printf("a=%d, b=%d\n", a, b);
return 0;
}
3つ目はポインタ型の誤用です。
#include <stdio.h>
int main(void) {
char *s = "Hello";
// NG: %d は整数用。ポインタを出したいなら %p を使う。
printf("アドレス? %d\n", s); // 未定義動作
// OK:
printf("アドレス: %p\n", (void *)s);
return 0;
}
書式文字列がユーザー入力のまま使われると、%nなどを悪用した脆弱性につながる場合があります。
信頼できない入力をそのままprintfに渡さないようにしましょう。
よく使うprintf書式指定子の実例集
ここからは「そのままコピペして使える」ことを意識して、典型的な使い方をまとめます。
整数を桁揃えして表示する例

#include <stdio.h>
int main(void) {
int values[] = {1, 20, 300, 4000};
int i;
printf("幅4で右寄せ:\n");
for (i = 0; i < 4; i++) {
// 幅4で右寄せ
printf("%4d\n", values[i]);
}
printf("\n幅4で左寄せ:\n");
for (i = 0; i < 4; i++) {
// 幅4で左寄せ
printf("%-4d\n", values[i]);
}
return 0;
}
幅4で右寄せ:
1
20
300
4000
幅4で左寄せ:
1
20
300
4000
データの一覧を表示するときに、桁揃えができていると可読性が大きく向上します。
マイナス値とプラス値を揃えて表示する例
#include <stdio.h>
int main(void) {
int vals[] = {-5, 10, -123, 45};
int i;
printf("符号あり右寄せ(幅5):\n");
for (i = 0; i < 4; i++) {
printf("%+5d\n", vals[i]); // + フラグで正数にも+を付ける
}
printf("\n符号位置をそろえる(空白フラグ):\n");
for (i = 0; i < 4; i++) {
printf("% 5d\n", vals[i]); // 空白フラグで正数は先頭に空白
}
return 0;
}
符号あり右寄せ(幅5):
-5
+10
-123
+45
符号位置をそろえる(空白フラグ):
-5
10
-123
45
+フラグは「+/-をはっきり表示したいとき」、空白フラグは「正数はスペースで揃えたいとき」に使い分けます。
小数点以下の桁数をそろえる例

#include <stdio.h>
int main(void) {
double vals[] = {3.1, 12.3456, 0.5, 123.0};
int i;
printf("デフォルト(%%f):\n");
for (i = 0; i < 4; i++) {
printf("%f\n", vals[i]);
}
printf("\n小数2桁(%%.2f):\n");
for (i = 0; i < 4; i++) {
printf("%.2f\n", vals[i]);
}
printf("\n幅8, 小数2桁(%%8.2f):\n");
for (i = 0; i < 4; i++) {
printf("%8.2f\n", vals[i]);
}
return 0;
}
デフォルト(%f):
3.100000
12.345600
0.500000
123.000000
小数2桁(%.2f):
3.10
12.35
0.50
123.00
幅8, 小数2桁(%8.2f):
3.10
12.35
0.50
123.00
小数点以下の桁をそろえることで、値の比較や異常値の発見がしやすくなります。
16進ダンプ風にバイト列を表示する例

メモリの内容を確認したいとき、16進数のダンプ表示が役立ちます。
#include <stdio.h>
int main(void) {
unsigned char data[] = {0x00, 0x1F, 0xA0, 0xB3, 0xFF, 0x10, 0x20, 0x30};
int i;
int size = (int)(sizeof(data) / sizeof(data[0]));
printf("バイト列(16進ダンプ風):\n");
for (i = 0; i < size; i++) {
// 2桁の16進数でゼロ埋め表示
printf("%02X ", data[i]);
// 8バイトごとに改行
if ((i + 1) % 8 == 0) {
printf("\n");
}
}
// 末尾が8の倍数でなければ改行を入れる
if (size % 8 != 0) {
printf("\n");
}
return 0;
}
バイト列(16進ダンプ風):
00 1F A0 B3 FF 10 20 30
%02X を使うと「幅2桁・ゼロ埋め・16進・大文字」で表示できます。
バイト列のデバッグでは定番のテクニックです。
文字列一覧を表形式で出力する例

#include <stdio.h>
int main(void) {
const char *names[] = {"Alice", "Bob", "Catherine", "Dan"};
const char *notes[] = {"OK", "NG: timeout", "OK", "pending"};
int i;
printf("+----+------------+--------------+\n");
printf("| ID | 名前 | メモ |\n");
printf("+----+------------+--------------+\n");
for (i = 0; i < 4; i++) {
// IDを2桁右寄せ、名前を幅10左寄せ、メモを幅12左寄せ
printf("| %2d | %-10.10s | %-12.12s |\n",
i, names[i], notes[i]);
}
printf("+----+------------+--------------+\n");
return 0;
}
+----+------------+--------------+
| ID | 名前 | メモ |
+----+------------+--------------+
| 0 | Alice | OK |
| 1 | Bob | NG: timeout |
| 2 | Catherine | OK |
| 3 | Dan | pending |
+----+------------+--------------+
ここでは幅指定と精度指定(%.10sなど)で、長すぎる名前やメモを切りつつ列をそろえる工夫をしています。
デバッグで値をまとめて表示するテクニック
デバッグ時には複数の値を1行にまとめて分かりやすく表示すると効率的です。
#include <stdio.h>
int main(void) {
int count = 10;
int err = -2;
double avg = 3.14159;
const char *status = "running";
printf("[DEBUG] count=%d, err=%d, avg=%.3f, status=%s\n",
count, err, avg, status);
// アドレスや16進数も一緒に表示
printf("[DEBUG] &count=%p, err(hex)=%#x\n",
(void *)&count, (unsigned int)err);
return 0;
}
[DEBUG] count=10, err=-2, avg=3.142, status=running
[DEBUG] &count=0x7ffee3cdd47c, err(hex)=0xfffffffe
「[DEBUG]」などのプレフィックスを付け、変数名と値をペアで表示すると、ログを後から見返したときに非常に分かりやすくなります。
16進数やアドレスも併せて出すことで、バグの原因特定に役立ちます。
まとめ
printfの書式指定子は一見複雑ですが、基本の%d/%u/%x/%s/%f/%pを押さえ、フィールド幅・精度・フラグの組み合わせ方に慣れてしまえば、実務でも強力な武器になります。
特に桁揃え・小数桁数の調整・16進ダンプ・テーブル表示・デバッグメッセージの5パターンを自分の定番スタイルとして持っておくと、毎日の開発がぐっと楽になります。
この記事のサンプルコードをベースに、自分のプロジェクト向けフォーマットを整えていきましょう。
