数値を文字列に整形して表示や保存をしたい時、C言語ではsprintf
がとても便利です。
桁幅の指定やゼロ埋め、符号の付与、小数点以下の桁数調整などを柔軟に扱えます。
ただしバッファサイズの管理が重要です。
本記事では初心者向けに、基本から安全な使い方まで丁寧に解説します。
sprintf()の基本
sprintf()の書式
sprintf
は、フォーマットに従って文字列を作り、指定したバッファへ書き込みます。
関数プロトタイプは次の通りです。
#include <stdio.h>
int sprintf(char *str, const char *format, ...);
返り値は書き込まれた文字数(終端のヌル文字は含まない)で、エンコーディングエラー時は負数を返します。
バッファ終端にはヌル文字(‘\0’)が自動で付与されます。
最小の使用例
#include <stdio.h>
int main(void) {
int n = 42;
char buf[32]; // 出力先のバッファ(十分な大きさを確保)
int len = sprintf(buf, "%d", n); // 整数nを文字列に変換してbufへ
printf("buf=\"%s\" (len=%d)\n", buf, len); // 結果を画面に出力して確認
return 0;
}
buf="42" (len=2)
ここで使っている"%d"
は符号付き10進整数を意味します。
数値を文字列に変換する流れ
1) バッファを用意 → 2) フォーマット指定子を決める → 3) sprintf
を呼ぶ → 4) 返り値を確認という流れです。
#include <stdio.h>
int main(void) {
int userId = 7;
double pi = 3.14159;
char idbuf[16];
char pibuf[16];
// 3桁ゼロ埋めのID (007 のように出す)
sprintf(idbuf, "ID:%03d", userId);
// 小数点以下2桁の実数
sprintf(pibuf, "PI=%.2f", pi);
printf("%s\n", idbuf);
printf("%s\n", pibuf);
return 0;
}
ID:007
PI=3.14
ゼロ埋めや小数点以下の桁数はフォーマット指定子の一部として表現します。
詳しくは後述します。
バッファサイズとヌル終端
sprintf
はサイズ制限なしで書くため、バッファが小さいとバッファあふれが起きます。
実用ではsnprintf
の使用を推奨します。
以下は安全にサイズを制限し、ヌル終端も保証する例です。
#include <stdio.h>
int main(void) {
char small[5];
char big[32];
// small は 4文字 + ヌルの5バイトしか入らない
// "Value=12345" は 11 文字なので切り詰められる
int r1 = snprintf(small, sizeof(small), "Value=%d", 12345);
// 十分な大きさのバッファを使えば切り詰めは起きない
int r2 = snprintf(big, sizeof(big), "Value=%d", 12345);
printf("small=\"%s\" (必要文字数=%d, 実際に格納された最大=%zu)\n",
small, r1, sizeof(small) - 1);
printf("big =\"%s\" (必要文字数=%d)\n", big, r2);
return 0;
}
small="Valu" (必要文字数=11, 実際に格納された最大=4)
big ="Value=12345" (必要文字数=11)
バッファが足りないと切り詰められます。
必要文字数は返り値で分かるので、足りない場合は再確保などで対応します。
printfとの違い
printf
は標準出力に表示し、sprintf
はメモリ上の文字配列に書き込む点が異なります。
フォーマット指定や返り値の扱いは共通です。
#include <stdio.h>
int main(void) {
int x = 255;
char buf[32];
// 画面に直接出力
printf("printf: x=%d\n", x);
// 文字列として整形し、その後で出力
sprintf(buf, "sprintf: x=%d", x);
printf("%s\n", buf);
return 0;
}
printf: x=255
sprintf: x=255
画面に出す必要がなく、後で使い回したい場合はsprintf
/snprintf
が便利です。
フォーマット指定子の基本
整数のフォーマット
型と指定子は必ず一致させます。
不一致は未定義動作です。
- 符号付き10進:
%d
または%i
(推奨は%d
) - 符号なし10進:
%u
#include <stdio.h>
int main(void) {
int si = -42;
unsigned int ui = 4000000000u; // 32bit環境での例
char b1[32], b2[64];
sprintf(b1, "signed=%d", si);
sprintf(b2, "unsigned=%u", ui);
printf("%s\n", b1);
printf("%s\n", b2);
return 0;
}
signed=-42
unsigned=4000000000
16進・8進のフォーマット
- 16進:
%x
(小文字),%X
(大文字) - 8進:
%o
- 代替形式
#
で接頭辞を付加(0x
,0X
,0
)
#include <stdio.h>
int main(void) {
unsigned v = 255;
char hx1[32], hx2[32], oc1[32], oc2[32];
sprintf(hx1, "hex=%x", v); // 小文字
sprintf(hx2, "HEX=%#X", v); // 0X 接頭辞つき大文字
sprintf(oc1, "oct=%o", v);
sprintf(oc2, "OCT=%#o", v); // 0 接頭辞つき
printf("%s\n%s\n%s\n%s\n", hx1, hx2, oc1, oc2);
return 0;
}
hex=ff
HEX=0XFF
oct=377
OCT=0377
実数のフォーマット
- 固定小数点:
%f
- 指数表記:
%e
/%E
- 最適表記:
%g
/%G
- 既定の精度は小数点以下6桁です
#include <stdio.h>
int main(void) {
double d = 123.456;
char bf[64], be[64], bg[64];
sprintf(bf, "f=%f", d);
sprintf(be, "e=%e", d);
sprintf(bg, "g=%g", d);
printf("%s\n%s\n%s\n", bf, be, bg);
return 0;
}
f=123.456000
e=1.234560e+02
g=123.456
文字・文字列
- 文字:
%c
- 文字列:
%s
(ヌル終端'\0'
必須) - パーセント記号自体を出す:
%%
#include <stdio.h>
int main(void) {
char c = 'A';
char s[] = "Hello";
char bc[16], bs[32], bp[16];
sprintf(bc, "char=%c", c);
sprintf(bs, "str=%s", s);
sprintf(bp, "100%% OK");
printf("%s\n%s\n%s\n", bc, bs, bp);
return 0;
}
char=A
str=Hello
100% OK
型に合わせる指定子
長さ修飾子を組み合わせて正しい型を指定します。
%lld
(long long
), %zu
(size_t
)
#include <stdio.h>
int main(void) {
long long big = 1234567890123LL; // long long
size_t len = 5u; // 例として固定値
char b1[64], b2[64];
sprintf(b1, "big=%lld", big);
sprintf(b2, "len(size_t)=%zu", len);
printf("%s\n%s\n", b1, b2);
return 0;
}
big=1234567890123
len(size_t)=5
型と指定子のミスマッチは危険です。
特に64ビット整数は%lld
、サイズ関連は%zu
を使うなど、正しい修飾子を選びましょう。
以下は代表的な指定子まとめです。
分類 | 指定子 | 意味 | 例 |
---|---|---|---|
符号付き整数 | %d | 10進 | -42 |
符号なし整数 | %u | 10進 | 4000000000 |
16進整数 | %x/%X | 小文字/大文字 | ff/FF |
8進整数 | %o | 8進 | 377 |
実数 | %f | 固定小数点 | 3.140000 |
実数 | %e/%E | 指数表記 | 1.23e+03 |
実数 | %g/%G | 短い表記 | 123.45 |
文字 | %c | 1文字 | A |
文字列 | %s | ヌル終端文字列 | Hello |
パーセント | %% | % を出す | % |
また、長さ修飾子の例です。
修飾子 | 例 | 対応型の例 |
---|---|---|
h | %hd, %hu | short, unsigned short |
l | %ld, %lu | long, unsigned long |
ll | %lld, %llu | long long, unsigned long long |
z | %zu, %zd | size_t, ssize_t |
j | %jd, %ju | intmax_t, uintmax_t |
t | %td | ptrdiff_t |
常に「値の型」=「フォーマット(指定子+修飾子)」になるように合わせます。
桁数指定とゼロ埋めの使い方
桁幅の指定
%5d
のように数字を付けると最小幅を指定できます。
足りない分はスペースで埋まります。
#include <stdio.h>
int main(void) {
int v = 42;
char b1[16], b2[16];
sprintf(b1, "'%5d'", v); // 右寄せ(スペース詰め)
sprintf(b2, "'%3d'", 12345); // 幅より長い場合はそのまま(切り詰めない)
printf("%s\n%s\n", b1, b2);
return 0;
}
' 42'
'12345'
幅より長い内容は切り詰められない点に注意してください。
ゼロ埋め
%05d
のように0
フラグで左側を0で埋めることができます。
負数では符号が先頭に出てからゼロ埋めされます。
#include <stdio.h>
int main(void) {
char b1[16], b2[16];
sprintf(b1, "'%05d'", 42); // 00042
sprintf(b2, "'%05d'", -42); // -0042
printf("%s\n%s\n", b1, b2);
return 0;
}
'00042'
'-0042'
整数の精度(例: %.4d
)を指定した場合、ゼロ埋めフラグ0
は無視されます。
小数点以下の桁数
%.2f
のように精度を指定します。
実数では小数点以下の桁数、文字列では最大出力長になります。
#include <stdio.h>
int main(void) {
double pi = 3.14159;
char s[] = "ABCDEFGHI";
char b1[32], b2[32];
sprintf(b1, "%.2f", pi); // 小数点以下2桁に四捨五入
sprintf(b2, "%.5s", s); // 先頭5文字だけ
printf("%s\n%s\n", b1, b2);
return 0;
}
3.14
ABCDE
左寄せ
%-5d
のように-フラグで左寄せにできます。
#include <stdio.h>
int main(void) {
char b1[16];
sprintf(b1, "'%-5d'", 42);
printf("%s\n", b1);
return 0;
}
'42 '
符号を出す
%+d
で常に符号を出します。
% d
(先頭スペース)は正数時に空白を付けます。
#include <stdio.h>
int main(void) {
char p[16], s[16], n[16];
sprintf(p, "%+d", 42); // +42
sprintf(s, "% d", 42); // 42 (先頭に空白)
sprintf(n, "%+d", -7); // -7
printf("%s\n%s\n%s\n", p, s, n);
return 0;
}
+42
42
-7
見やすい数表にしたい時に便利です。
例で学ぶsprintf()の活用
ゼロ埋めIDを作る
IDを5桁にゼロ埋めし、接頭辞を付けます。
#include <stdio.h>
int main(void) {
int id = 42;
char out[32];
sprintf(out, "ID-%05d", id); // 5桁ゼロ埋め
printf("%s\n", out);
return 0;
}
ID-00042
固定桁で整形して並べると一覧が見やすくなります。
時刻を整形する
時と分と秒を2桁ゼロ埋めでつなぎます。
#include <stdio.h>
int main(void) {
int hh = 7, mm = 5, ss = 9;
char t[16];
sprintf(t, "%02d:%02d:%02d", hh, mm, ss);
printf("%s\n", t);
return 0;
}
07:05:09
桁幅とゼロ埋めの組み合わせが、時刻や日付の整形に適しています。
16進文字列に変換
バイト配列を16進の文字列へ変換します。
サイズは2文字×バイト数+終端が必要です。
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t bytes[] = {0xDE, 0xAD, 0xBE, 0xEF};
size_t n = sizeof(bytes) / sizeof(bytes[0]);
char hex1[2 * 4 + 1]; // "DEADBEEF" 用(8文字+終端)
char hex2[3 * 4 - 1 + 1]; // "de:ad:be:ef" 用(3n-1+終端)
// 大文字の連結 (各バイトを2桁で)
for (size_t i = 0; i < n; ++i) {
sprintf(hex1 + 2 * i, "%02X", bytes[i]);
}
// 小文字、":"区切りで連結
size_t pos = 0;
for (size_t i = 0; i < n; ++i) {
pos += sprintf(hex2 + pos, "%02x%s", bytes[i], (i + 1 < n) ? ":" : "");
}
printf("%s\n%s\n", hex1, hex2);
return 0;
}
DEADBEEF
de:ad:be:ef
書き込み位置を進めるテクニックを使うと、連結時も簡潔に書けます。
符号付きの出力
プラスもマイナスも見せたいときは%+
を使います。
#include <stdio.h>
int main(void) {
double t1 = 9.3, t2 = -2.1;
char b1[32], b2[32];
sprintf(b1, "%+.1fC", t1);
sprintf(b2, "%+.1fC", t2);
printf("%s, %s\n", b1, b2);
return 0;
}
+9.3C, -2.1C
傾きや差分などの表示に向いています。
バッファあふれ対策
安全な文字列整形にはsnprintf
を使い、返り値で切り詰めを検出します。
#include <stdio.h>
int main(void) {
char buf[16];
int need = snprintf(buf, sizeof(buf), "ID=%05d ITEM=%s", 123, "ABCDEFG");
int truncated = (need >= (int)sizeof(buf)); // 必要文字数 >= バッファ
printf("必要文字数=%d, 実際に格納=%zu, 切り詰め=%s\n",
need, sizeof(buf) - 1, truncated ? "YES" : "NO");
printf("buf=\"%s\"\n", buf);
return 0;
}
必要文字数=21, 実際に格納=15, 切り詰め=YES
buf="ID=00123 ITEM=A"
必要文字数need
が分かったら、ちょうど良いサイズで再確保する方法もあります。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *name = "Alice";
int id = 12345;
// 必要文字数を先に知る (終端を含まない)
int need = snprintf(NULL, 0, "user=%s id=%05d", name, id);
char *p = (char *)malloc((size_t)need + 1);
if (!p) return 1;
// ぴったりサイズで本番出力
snprintf(p, (size_t)need + 1, "user=%s id=%05d", name, id);
printf("%s\n", p);
free(p);
return 0;
}
user=Alice id=12345
常にサイズを意識し、返り値で切り詰めを検出することで、安全に整形できます。
まとめ
sprintf
は数値を柔軟に文字列へ整形できる強力な関数です。
整数や実数、16進数などの基本の指定子、桁幅・精度・ゼロ埋め・左寄せ・符号といったフラグを正しく使えば、目的の表記を簡単に得られます。
一方で最大の注意点はバッファサイズです。
実開発ではsnprintf
でサイズを制限し、返り値から切り詰めを検出するパターンを習慣化すると安全です。
まずは本記事のサンプルを写経し、型と指定子を正しく合わせる、幅と精度を意図通りに指定する、バッファあふれを防ぐの3点を身につけていきましょう。