【C言語】数値を文字列に変換する(snprintf関数の活用)

数値を文字列に変換する方法の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、数値を文字列に変換する方法について説明していきます!

このページの内容を理解していただければ、123 という整数を "123" という3文字の文字列に変換したり、123.45 という実数を "123.45" という6文字の文字列に変換したりすることが出来るようになります。

この数値の文字列の変換は難解な処理を記述して実現する必要もなく、非常に簡単かつ単純に実現することが可能です。しかもC言語にある程度慣れている方であれば、既に使い方を理解しているであろう関数と同様の使い方をするだけで変換を実現することが可能です!

ちなみに、1桁の数値を数字(文字)に変換する方法については下記で紹介しています。この方法を応用すれば複数桁の数値を文字列に変換することも可能ですが、このページで紹介する方法の方が簡単に変換が可能です。

数字と数値の変換方法の解説ページアイキャッチ 【C言語】数字⇔数値の変換方法

また、逆に文字列を数値に変換する方法は下記で紹介していますので、文字列の数値への変換を行いたい場合は下記ページを参考にしていただければと思います。

文字列の数値への変化方法の解説ページアイキャッチ 【C言語】文字列を数値に変換する方法(atoi・strtol など)

数値を文字列に変換する関数

では、最初に結論として、数値を文字列に変換する関数を紹介しておきます。

関数

下記の main を除く3つの関数が数値を文字列に変換する関数となります。main 関数では、これらの3つの関数の実行例を示しています。

数値を文字列に変換する関数
#include <stdio.h>

#define MAX_LEN (256)

void double2str(char str[], size_t len, double num) {
    snprintf(str, len, "%.2f", num);
}

void uint2str(char str[], size_t len, unsigned int num) {
    snprintf(str, len, "%u", num);
}

void int2str(char str[], size_t len, int num) {
    snprintf(str, len, "%d", num);
}

int main(void) {
    char str[MAX_LEN];
    
    double2str(str, MAX_LEN, 123.45);
    printf("%s\n", str);

    uint2str(str, MAX_LEN, 4294967295);
    printf("%s\n", str);

    int2str(str, MAX_LEN, -256);
    printf("%s\n", str);
}

数値を文字列に変換する関数は、数値を扱う変数や数値の型に応じて用意しています。具体的には、それぞれの関数は下記を行う関数となっています。

  • double2strdouble 型  (or float 型) を文字列に変換する
  • uint2strunsigned int 型を文字列に変換する
  • int2strint 型を文字列に変換する

double2str は浮動小数点数を文字列に変換する関数ですが、この関数では小数点以下第2桁までのみを文字列に変換するようにしています。文字列に変換する小数点以下の桁数を変更したい場合は %.2f2 の部分を変更してください。例えば %.5f に変更すれば小数点以下第5桁までが文字列に変換されるようになります。

スポンサーリンク

関数の使い方

これらの関数では第3引数で指定した数値が文字列に変換され、その結果が第1引数で指定されたアドレスに格納されます。main 関数では第1引数に配列 str の先頭アドレスを指定しているため、配列 str に文字列変換結果が格納されることになります。

整数の文字列への変換結果が配列strに格納される様子

つまり、main 関数は第3引数に数値を指定して各関数を実行することで、その数値の文字列への変換結果を str として得ることが出来ます。もちろん、上記の main 関数では単に printf で出力のみを行っていますが、例えば str を文字列長を取得する strlen の引数に指定したり、文字列の結合を行う strcat の引数に指定したりして利用することも可能です。

数値を文字列に変換した結果を通常の文字列同様に扱うことができることを示す図

また、上記の main 関数の例では第1引数に char 型の配列を指定していますが malloc 関数で確保したメモリのアドレスを指定するのでも OK です。

MEMO

malloc 関数って何?」と疑問を持たれた方は、このページを読み終わった後にでも下記ページを読んでみていただければと思います

使い方はちょっと難しいですが超便利な関数になります

malloc解説ページのアイキャッチ 【C言語】malloc関数(メモリの動的確保)について分かりやすく解説

ただし、第2引数には第1引数で指定する配列やメモリのサイズを指定する必要がある点に注意してください。これは、文字列への変換結果がサイズを超えて配列やメモリに格納されることを防ぐためになります。要はバッファーオーバーランをこれで防いでいます。

各関数の第2引数の説明図

これら3つの関数をコピペして使えば、数値を文字列に変換した結果を第1引数から取得することが可能となります。変換したい数値の型に応じて必要な関数を利用していただければと思います。

数値の文字列への変換の仕組み

続いて、先ほど紹介したような関数によって数値の文字列への変換が可能となる理由・仕組みについて説明していきます。

数値を文字列に変換する関数 で示した関数を見ていただければ分かる通り、数値の文字列への変換を実際に行っているのは snprintf 関数になります。

同様の役割を持つ関数としては sprintf もあって、これらの関数には「生成する文字列の長さの上限を指定可能か否か」の違いしかありません。指定可能なのが snprintf 関数の方で、この指定によってバッファーオーバーランを防ぐことが可能となっています。つまり、より安全なプログラムを開発するためには snprintf 関数の方の使用が推奨されます。ということで、ここからは基本的に snprintf 関数の方を使って数値の文字列への変換の仕組みを説明していきます。

数値の文字列の変換は printf でも行われる

で、まず注目していただきたいのが、先ほど紹介した snprintf の「関数名」になります。"snprintf" には、あなたにとっても馴染みのある関数の名前が含まれていることが確認できると思います。そうです、printf です!

snprintf関数がprintf関数に似た関数であることを示す図

この関数名からも分かるように snprintfprintf に似た関数となります。機能的な観点で考えると、これらの関数の違いは文字列の出力先のみと考えて良いでしょう。そして、printf は数値を文字列に変換することが可能です。それと同様に snprintf でも数値を文字列に変換することが可能となっています。

printf での数値の文字列への変換

まず printf に注目すると、printf 関数では下記のように第1引数で指定するフォーマットに %d などの変換指定子を指定することで、第2引数以降に指定した “数値そのもの” や “変数に格納されている数値” を変換指定子部分に埋め込むことが出来ます(下記の場合は変数 num に格納されている数値が %d 部分に埋め込まれる)。

printfの使い方
printf("answer = %d\n", num);

この時、第2引数以降に指定した数値や変数は、変換指定子に応じた変換が行われてから変換指定子部分に埋め込まれることになります。

MEMO

この変換指定子に応じた変換については後述で解説します

まずは変換指定子が %d であることを前提に解説していきます

%d は第2引数で以降に指定された数値を int 型に変換する変換指定子になります

そして、その埋め込んだ結果となる文字列が標準出力に出力されることになります。

変換指定子部分に引数で指定した数値が埋め込まれる様子

また、下記のように第1引数のフォーマットに “変換指定子のみ” を引数に指定した場合は、第2引数で指定された数値が %d 部分に埋め込まれるため、単に第2引数で指定された数値を文字列化した結果のみが標準出力に出力されることになります。例えば下記を実行すれば、文字列 "123" が標準出力に出力されることになります。

変換指定子のみの指定
int num = 123;
printf("%d", num);

そして、この出力結果こそが、このページで実現しようとしている数値の文字列への変換結果となります。

つまり、printf 関数では、このページで実現しようとしている「数値の文字列の変換」が既に実現されていることになります。具体的には、第1引数のフォーマットに変換指定子を1つのみ指定して実行すれば第2引数に指定した数値を文字列に変換した結果のみが出力されることになります。この変換結果を配列等に出力することができれば数値の文字列の変換結果の取得を実現することができます。

printf の変換結果の出力先は標準出力

ですが、残念ながら printf の場合は 変換結果の出力先は標準出力です…。ただ、数値の文字列の変換だけであれば既に行われるようになっているため、あとは出力先さえ配列等に変更してやれば変換結果を取得してそれをプログラムから利用することが出来るようになります。

printfでの数値の文字列への変換結果の出力先と、このページで実現したい出力先の違いを示す図

スポンサーリンク

snprintf で変換結果を配列に出力する

で、ここで活躍するのが snprintf 関数となります。snprintf は、言ってしまえば出力先を “配列” へ変更した printf 関数となります。したがって、この関数を利用することで printf 関数同様に数値を文字列に変換することができ、さらに、その結果を配列に出力することが出来ることになります。

前述の通り、printf の場合、第1引数のフォーマットに変換指定子を1つのみ指定して実行すれば第2引数に指定した数値を文字列に変換した結果のみが出力されることになります。

それに対し、snprintf 関数の場合、フォーマットを指定するのが第3引数、文字列に変換したい数値を指定するのが第4引数となりますが、同様の指定の仕方をすることで、第1引数に指定したアドレスに数値を文字列に変換した結果が出力されることになります。つまり、第1引数に配列の先頭アドレスを指定してやれば、数値を文字列に変換した結果が配列に格納されることになります。

snprintfの動作の説明図

snprintf の使い方

次は、もう少し具体的に snprintf 関数の使い方を確認していきましょう。

snprintf 関数の使い方は、関数名と同様に printf 関数と似ています。

snprintf 関数と printf 関数は共に stdio.h で宣言されており、これらの宣言は具体的に下記のようなものになっています。ちなみに、... は可変長引数を示す記号であり、ここには可変長個の引数を指定することが可能です。printf 関数や snprintf 関数においては、通常 format 引数に指定したフォーマットに含まれる “変換指定子と同じ数だけの引数” を可変長引数として指定することになります。が、今回の数値への文字列の変換のみを実現したいのであれば、... 部分には変換したい数値の1つのみを指定してやれば良いことになります。

snprintfとprintf
int printf(const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

snprintf 関数は第1引数に配列やメモリ等のアドレスを指定し、第2引数には基本的に第1引数に指定する配列やメモリのサイズを指定すると考えてよいです。

さらに、snprintf 関数の第3引数には出力する文字列のフォーマットを指定します。そして、このフォーマットの中に %d%f などの変換指定子を含ませることで、第4引数以降に指定したデータを文字列に変換した結果が変換指定子の部分に埋め込められた文字列が出力されることになります。前述のとおり、この出力先は第1引数に指定したアドレスになります。

で、お気づきのとおり、printf 関数の第1引数以降と snprintf 関数の第3引数以降は同じ意味合いの引数になります。

したがって、snprintf 関数の第3引数以降は printf の第1引数以降の引数と同様の指定によって利用することが可能です。そして、今回目的としている「数値の文字列への変換」を行う際には、フォーマットには1つの変換指定子のみを " (ダブルクォーテーション) で囲って指定してやれば良く、第4引数には文字列に “変換したい数値” や “変換したい数値を格納した変数” を指定します。

このように引数を指定して実行することで、snprintf 関数で第4引数に指定した数値が文字列に変換された結果が第1引数に格納されることになります。そして、関数実行側で配列に格納された文字列を利用して様々な処理を実現することが可能となります。

snprintfの実行によって第1引数のアドレスから変換結果の文字列を取得可能であることを示す図

例えば下記の処理を実行すれば、data に格納される整数が文字列に変換されてアドレス str に格納されます。C言語において、配列名は配列の先頭のアドレスとして扱われますので、要は配列 strの先頭から data の文字列への変換結果("123")が格納されることになります。

snprintfの実行例
char str[100];
int num = 123;
snprintf(str, 100, "%d", data);

snprintf 関数で数値を文字列に変換する手順

ここまで説明してきたように、snprintf 関数の「”変換指定子に数値が埋め込まれた文字列” が配列等に格納される」という特徴を利用すれば数値の文字列への変換を簡単に実現することが出来ます。

この時の手順を具体的に示せば下記となります。

  1. 配列やメモリを用意する
    • 配列は変数宣言で、メモリは malloc 関数で用意可能
  2. 下記のように引数を指定して snprintf 関数を実行する
    • 第1引数:1. で用意した配列やメモリのアドレス
    • 第2引数:1. で用意した配列やメモリのサイズ
    • 第3引数:”変換指定子のみの文字列” ("%d""%f" など)
    • 第4引数:変換したい数値 or 変換したい数値を格納した変数

snprintf 関数実行時の注意点

この手順で数値を文字列に変換する際は下記の点に注意してください。

  • 第1引数には 変換後の文字列の長さ + 1 以上のサイズの配列(メモリ)を指定する
  • 変換したい数値に合わせた変換指定子を指定する

第1引数に指定する配列のサイズ

まず、snprintf 関数の数値の文字列変換結果の格納先は第1引数に指定するアドレスになります。配列名を指定することで配列の先頭アドレスを指定することが可能となりますが、この配列には変換結果となる文字列よりも大きなサイズが必要となります。より具体的には、数値を文字列への変換結果となる「文字 + ヌル文字」が格納されることになります。なので、数値の桁数 + 1 のサイズが必要となります。

変換結果となる文字列格納先の配列に必要となるサイズ

サイズが小さいとバッファーオーバーランが発生して他の配列や変数を意図せず変更してしまう可能性があるので注意してください。

バッファーオーバーランが発生する様子

また、snprintf 関数では第1引数に指定するアドレスに格納されるデータの個数は第2引数に指定したサイズが上限となります。したがって、第2引数に “第1引数に指定するアドレスの配列やメモリのサイズ” を指定してやればバッファーオーバーラン自体は防げます。

snprintf関数の第2引数に配列のサイズを指定することでバッファーオーバーランを防ぐ様子

が、変換後の文字列が途中で切れてしまう可能性もあるため、それを防ぐために十分なサイズの配列やメモリを予め用意するとともに、第2引数にそれらのサイズを指定してバッファーオーバーラン対策を行うのが良いと思います。

どうしても考慮が抜けがちになってしまうのがヌル文字になります。C言語で文字列を扱う際にはヌル文字を考慮しながらプログラムを開発していく必要があります。ヌル文字については下記ページで開設していますので、ヌル文字についてご存じない方は別途下記ページを参照していただければと思います。

ヌル文字の解説ページのアイキャッチ 【C言語】ヌル文字(NULL文字)とは?

先ほども少し触れましたが、特に使用可能なメモリに制限が無いのであれば十分な大きな配列やメモリを用意し、そのアドレスをsnprintf 関数の第1引数に指定してやるのが良いと思います。そして、その配列やメモリのサイズを第2引数に指定してバッファーオーバーラン対策を行います。

数値を文字列に変換する関数 で示した main 関数ではサイズが 256 の配列を用意するようにしていまので、255 桁の数値を文字列に変換することが可能となっています。ここまで桁数の多い数値を扱うことはほとんどないため、十分なサイズの配列が用意されていることが確認できると思います。

もし、256 桁以上の桁数の数値を文字列に変換したとしても、第2引数で指定したサイズの文字列のみが第1引数で指定されるアドレスに格納されることになるため、バッファーオーバーランは防ぐことが出来ることになります(255 桁の文字 + ヌル文字が格納される)。

第3引数に指定する変換指定子

また、ここまで説明してきたように第3引数のフォーマットに含ませる変換指定子は、変換したい数値や変数の型に合わせて適切に選択する必要がある点にも注意が必要です。

snprintf では第4引数以降に指定した数値が “変換指定子に対応する型” に変換されることになります。そして、その後に変換指定子部分に変換後の値が埋め込まれることになります。したがって、snprintf で第4引数に指定する数値や変数の型と第3引数に指定するフォーマットの変換指定子に対応する型とが異なる場合、無理やりおかしな数値に変換されてしまった結果が変換指定子部分に埋め込まれることになります。

変換指定子に対応する型に数値が変換される様子

ということで、おかしな変換が行われないように「文字列に変換したい数値や変数の型」と「変換指定子に対応する型」は合わせておく必要があります。

変換指定子の代表例には下記のようなものが挙げられ、コロンの前側に変換指定子を、その変換指定子に対応する型をコロンの後ろ側に記載しています。例えば、int 型の変数に格納された数値を文字列に変換するのであれば、変換指定子による変換後も int 型であることが望ましいため %d を指定しておく必要があることになります。

  • %dint
  • %uunsigned int
  • %ffloat 型・double

例えば下記のように第4引数に 1.23 指定しているのに第3引数に "%d" を指定すると、1.23 が無理矢理 int に変換されてしまうことになります。浮動小数点数を文字列に変換しようとしているわけですから、文字列への変換を行うためには第3引数には "%f" を指定してやるのが正解となります。

不適切な変換指定子の指定
char str[MAX_LEN];
snprintf(str, MAX_LEN, "%d", 1.23);
printf("%s\n", str); // 1431666096が出力される!

このように、snprintf 関数を利用して数値を文字列に変換する際には、変換したい数値に合わせて適切な変換指定子を指定するようにしてやる必要がある点にも注意しましょう。

適切でない変換指定子が指定された場合は下記のような警告が出力されることになると思いますので、特にコンパイル時の警告はしっかりチェックするようにしましょう(今回に限らず、警告は常にチェックする方が良いですが…)。

warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘double’ [-Wformat=]
   24 |     snprintf(str, MAX_LEN, "%d", 1.23);
      |                             ~^   ~~~~
      |                              |   |
      |                              int double
      |                             %f

このような制限があるため、数値を文字列に変換する関数 では変換したい数値に合わせた変換指定した指定できるよう、関数を分けて用意しています(double (float)・unsigned intint の3種類)。

また、これも printf 同様に、変換指定子で「文字列に変換する小数点以下の桁数」を指定することも可能です。例えば 数値を文字列に変換する関数double2str では変換指定子に %.2f を指定しているため、文字列に変換される小数点以下の桁数は 2 桁固定になります。%.2f2 の部分を変更することで文字列に変換する小数点以下の桁数を変更することが可能ですので、このあたりは好みに合わせて指定していただければと思います。特に浮動小数点数の場合は誤差が含まれて変換後の文字列に余計な 0 が付加されることが多いので、それが気になる場合は小数点以下の桁数を変換指定子で指定して固定化してやることをオススメします。

まとめ

このページでは、数値を文字列に変換する方法について説明しました!

snprintf を利用することで「数値の文字列への変換」を実現することが可能となります。snprintfprintf と使い方が似ているので簡単に使いこなすことが出来るようになると思います。他にも数値を文字列に変換する方法はありますが、おそらく今回紹介した snprintf を利用する変換方法が一番楽だと思います。

例えば、下記ページでは1桁の数値を1つの数字に変換する方法を紹介していますが、これを数値の全桁に対して繰り返せば数値の文字列への変換は実現できるでしょう。この方法はC言語での数値や数字の扱いを理解するためには向いていますが、数値の文字列への変換を実現するだけであれば snprintf を使う方が楽です。

数字と数値の変換方法の解説ページアイキャッチ 【C言語】数字⇔数値の変換方法

ただし、楽ではありますが、バッファーオーバーランの発生には気を付ける必要がありますし、変換する数値に合わせて適切に変換指定子を指定する必要がある点にも注意が必要になります。

プログラミングをしていれば数値を文字列に変換したくなるケースも多々ありますので、是非 snprintf については覚えておきましょう!また、逆に文字列を数値に変換したくなるケースも多いと思います。この文字列から数値への変換方法は下記ページで紹介していますので、興味があれば是非下記ページも読んでみてください!

文字列の数値への変化方法の解説ページアイキャッチ 【C言語】文字列を数値に変換する方法(atoi・strtol など)

オススメの参考書(PR)

C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!

まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。

  • 参考書によって、解説の仕方は異なる
  • 読み手によって、理解しやすい解説の仕方は異なる

ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?

それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。

なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。

特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。

もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!

入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。

https://daeudaeu.com/c_reference_book/

同じカテゴリのページ一覧を表示