【C言語】数値を 2進数 8進数 10進数 16進数 文字列に変換する方法

C言語で数値を16進数や8進数の文字列や配列に変換するにはsprintfsnprintf関数を用いることが有効です。

// #include <stdio.h> // sprintf, snprintf
char s[16];
snprintf(s, 16, "%x", 15); // s: "f" (16進数)
snprintf(s, 16, "%u", 99); // s: "99"(10進数)
snprintf(s, 16, "%o", 10); // s: "12"(8進数)

snprintfの第一引数と第二引数に変換後の文字列の書き込み先とそのバッファーサイズを指定します。第三引数には各n進表記に対応する変換指定子(16進: %x, 10進: %u, 8進: %o)を指定し、第四引数に変換したい数値を指定します。

指定された数値はunsigned int型の符号無し整数として解釈されます。なお、char型やshort型、long型の値を変換する場合には長さ修飾子(char: hh, short: h, long: l)を付加する必要があります。

char s[17];
unsigned long ul = 0xffffffffeeeeeeee; // 64bit(sizeof(ul) == 8)と仮定
snprintf(s, 17, "%lx", ul); // "ffffffffeeeeeeee"
snprintf(s, 17, "%x" , ul); // "eeeeeeee"(32bit分しか読み取られないため)

char c = 0xFF;
snprintf(s, 17, "%hhx", c); // "ff"
snprintf(s, 17, "%x", c);   // "ffffffff"(`c`がint型の`-1`に符号拡張されているため)
snprintf(s, 17, "%x", (unsigned char)c);  // "ff"(明確な方法ではないが期待値は得られる)

sprintf関数の場合は第二引数のバッファサイズを指定することができなくなります。書き込み先のバッファサイズを超えた書き込みが発生する恐れがあるため注意して利用する必要があります。このバッファオーバーフローの危険性を回避するためには、変換後の文字列長を予め想定して大きめのバッファを確保すると良いでしょう。

目次

16進数を大文字として変換する

変換指定子に大文字の%Xを指定することで、大文字表記による変換が行われるようになります。

char s[9];
snprintf(s, 9, "%x", 254);   // "fe"
snprintf(s, 9, "%X", 254);   // "FE"
snprintf(s, 9, "%#X", 254);  // "0XFE"(記号も大文字に変換される)
snprintf(s, 9, "0x%X", 254); // "0xFE"(記号を個別に記述して対処)

16進数 8進数の基数を表す記号を表記する

16進数と8進数でそれぞれの基数を表す記号を付加させたい場合には、#フラグを用いた代替形式による変換が必要となります。

char s[9];
snprintf(s, 9, "%#x", 17); // "0x11"(16進数)
snprintf(s, 9, "%#o", 10); // "012" (8進数)

変換元の数値がゼロだった場合には記号が付加されないため注意してください。

また変換指定子に大文字の16進表記(%X)を指定した場合には、#フラグによる符号も同様に大文字に変換されてしまうため注意してください。この場合は0xを明示的に記述して対処する必要があります

snprintf(s, 9, "%#X", 15);  // "0XF"(16進数)
snprintf(s, 9, "0x%X", 15); // "0xF"(16進数)

16進数 8進数のゼロ埋め 左詰め 右詰め

10進数のゼロ詰めや左埋め/右埋めを行う際と同様に、0, -フラグやフィールド幅の指定が可能となっています。0フラグでゼロ埋め、-フラグで左詰めを指示できます。フラグが指定されなかった場合には指定されたフィールド幅で右詰めが行われます。

以下の例は、4文字分の幅でゼロ詰めと左埋め/右埋めを行った例です。

/* 16進数 */
char s[9];
snprintf(s, 9, "%04x", 15); // "000f"(ゼロ埋め)
snprintf(s, 9, "%-4x", 15); // "f   "(左詰め)
snprintf(s, 9, "%4x", 15);  // "   f"(右詰め)

/* 8進数 */
snprintf(s, 9, "%04o", 15); // "0017"(ゼロ埋め)
snprintf(s, 9, "%-4o", 15); // "17  "(左詰め)
snprintf(s, 9, "%4o", 15);  // "  17"(右詰め)

16進数で#フラグによる代替形式を指定した場合、意図したようなゼロ埋めが行われない場合があります。この場合はゼロ埋め後の値に0x記号を付加させる必要があります。

snprintf(s, 9, "%#04x", 15); // "0x0f"
snprintf(s, 9, "%#04x", 0);  // "0000"

snprintf(s, 9, "0x%04x", 15); // "0x000f"
snprintf(s, 9, "0x%04x", 0);  // "0x0000"

2進数への文字列変換について

C言語の標準関数では2進数への文字列変換を行うことができないため、代替処理を独自に記述する必要があります。環境によっては# itoa関数による 2進数 8進数 16進数 文字列変換を用いることもできますが、処理系依存の関数となっているため注意が必要です。

以下のサンプルは2進数文字列へ変換例です。上位から下位のビットを順次文字列へと変換しています。

#include <limits.h> // CHAR_BIT, UINT_MAX

void itoa_bin(char *s, unsigned int v) {
  unsigned int mask = (unsigned int)1 << (sizeof(v) * CHAR_BIT - 1);
  do *s++ = mask & v ? '1' : '0'; while (mask >>= 1);
  *s = '\0';
}

int main() {
  unsigned int i = UINT_MAX - 1;
  char s[33]; // 33 == (sizeof(unsigned int) * CHAR_BIT + 1)
  itoa_bin(s, i); // s: "11111111111111111111111111111110"
}

次のような万能関数を自作することも可能です。

void itob(char *s, int n, unsigned long long v) {
  if (n > 0) {
    unsigned long long f = 1ULL << (n - 1);
    do *s++ = f & v ? '1' : '0'; while (f >>= 1, --n);
  } else if (n != 0) {
    char *b = s;
    do *s++ = '0' + v % 2; while ((v >>= 1) && ++n);
    for (char c, *e = s - 1; b < e; b++, e--) c = *b, *b = *e, *e = c;
  }
  *s = '\0';
}

int main() {
  char s[9];
  itob(s,  4, 0b0010); // s: "0010"
  itob(s, -4, 0b0010); // s:   "10"
  itob(s,  8, -2);     // s: "11111110"
}

itoa関数による 2進数 8進数 16進数 文字列変換

itoa関数による文字列変換も可能ですが非標準関数であるため、移植性に問題があります。処理系依存の挙動を取る場合があったり、環境によっては利用できない場合があります。また環境によって対応可能なn進法や引数、フォーマットも異なるため注意が必要です。冒頭で紹介したsnprintf関数の利用を推奨します。

char s[32];
int n = sizeof(s) - 1;
itoa(9, s, n, 8);   // s: "11"(8進数)
itoa(31, s, n, 16); // s: "1F"(16進数)
itoa(9, s, n, 2);   // s: "1001"(2進数)
広告