閉じる

【C言語】ゼロ埋めや桁数指定もOK!sprintf()のフォーマットをわかりやすく解説

数値を文字列に整形して表示や保存をしたい時、C言語ではsprintfがとても便利です。

桁幅の指定やゼロ埋め、符号の付与、小数点以下の桁数調整などを柔軟に扱えます。

ただしバッファサイズの管理が重要です。

本記事では初心者向けに、基本から安全な使い方まで丁寧に解説します。

sprintf()の基本

sprintf()の書式

sprintfは、フォーマットに従って文字列を作り、指定したバッファへ書き込みます。

関数プロトタイプは次の通りです。

C言語
#include <stdio.h>

int sprintf(char *str, const char *format, ...);

返り値は書き込まれた文字数(終端のヌル文字は含まない)で、エンコーディングエラー時は負数を返します。

バッファ終端にはヌル文字(‘\0’)が自動で付与されます。

最小の使用例

C言語
#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) 返り値を確認という流れです。

C言語
#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の使用を推奨します。

以下は安全にサイズを制限し、ヌル終端も保証する例です。

C言語
#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メモリ上の文字配列に書き込む点が異なります。

フォーマット指定や返り値の扱いは共通です。

C言語
#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
C言語
#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)
C言語
#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桁です
C言語
#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'必須)
  • パーセント記号自体を出す: %%
C言語
#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)

C言語
#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を使うなど、正しい修飾子を選びましょう。

以下は代表的な指定子まとめです。

分類指定子意味
符号付き整数%d10進-42
符号なし整数%u10進4000000000
16進整数%x/%X小文字/大文字ff/FF
8進整数%o8進377
実数%f固定小数点3.140000
実数%e/%E指数表記1.23e+03
実数%g/%G短い表記123.45
文字%c1文字A
文字列%sヌル終端文字列Hello
パーセント%%% を出す%

また、長さ修飾子の例です。

修飾子対応型の例
h%hd, %hushort, unsigned short
l%ld, %lulong, unsigned long
ll%lld, %llulong long, unsigned long long
z%zu, %zdsize_t, ssize_t
j%jd, %juintmax_t, uintmax_t
t%tdptrdiff_t

常に「値の型」=「フォーマット(指定子+修飾子)」になるように合わせます

桁数指定とゼロ埋めの使い方

桁幅の指定

%5dのように数字を付けると最小幅を指定できます。

足りない分はスペースで埋まります。

C言語
#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で埋めることができます。

負数では符号が先頭に出てからゼロ埋めされます。

C言語
#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のように精度を指定します。

実数では小数点以下の桁数、文字列では最大出力長になります。

C言語
#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のように-フラグで左寄せにできます。

C言語
#include <stdio.h>

int main(void) {
    char b1[16];
    sprintf(b1, "'%-5d'", 42);
    printf("%s\n", b1);
    return 0;
}
実行結果
'42   '

符号を出す

%+d常に符号を出します。

% d(先頭スペース)は正数時に空白を付けます。

C言語
#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桁にゼロ埋めし、接頭辞を付けます。

C言語
#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桁ゼロ埋めでつなぎます。

C言語
#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文字×バイト数+終端が必要です。

C言語
#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

書き込み位置を進めるテクニックを使うと、連結時も簡潔に書けます。

符号付きの出力

プラスもマイナスも見せたいときは%+を使います。

C言語
#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を使い、返り値で切り詰めを検出します。

C言語
#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が分かったら、ちょうど良いサイズで再確保する方法もあります。

C言語
#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点を身につけていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!