[C言語のはじめ方] Part24: 数値変換・標準関数(atoi, atof, rand)

C言語

文字列を数値に変換したり、ランダムな数を生成したりする方法を学びましょう。

これまでのステップで、ファイル操作や基本的な文字列操作を学びましたね。今回は、プログラムでよく使われる便利な標準ライブラリ関数の中から、文字列を数値に変換する関数と、ランダムな数(乱数)を生成する関数を見ていきます。これらを使いこなせると、ユーザーからの入力を計算に使ったり、ゲームやシミュレーションでランダムな要素を加えたりできるようになりますよ!🚀

1. 文字列を整数に変換: atoi()

プログラムでは、ユーザーが入力した文字列(例: “123”)を、計算で使える数値(例: 123)に変換したい場面がよくあります。そんな時に役立つのが atoi 関数です。

atoi は “ASCII to integer” の略で、文字列を int 型の整数に変換します。この関数を使うには、<stdlib.h> ヘッダーファイルをインクルードする必要があります。

atoi() 関数の使い方

#include <stdlib.h> // atoi を使うために必要

int atoi(const char *str);
  • 引数 str: 変換したい文字列へのポインタ。
  • 戻り値: 変換された int 型の数値。変換できない場合やエラーの場合の動作は標準で定められていません(多くの場合 0 が返りますが、保証されません)。

コード例

#include <stdio.h>
#include <stdlib.h>

int main() {
    char str_num[] = "12345";
    int num = atoi(str_num);

    printf("文字列 \"%s\" を整数に変換すると %d です。\n", str_num, num);

    char str_invalid[] = "abc";
    int invalid_num = atoi(str_invalid);
    printf("文字列 \"%s\" を変換しようとすると %d になります。\n", str_invalid, invalid_num); // 通常 0

    char str_partial[] = "99yen";
    int partial_num = atoi(str_partial);
    printf("文字列 \"%s\" を変換しようとすると %d になります。\n", str_partial, partial_num); // 99

    return 0;
}
注意点:
  • atoi は非常にシンプルですが、エラーチェック機能が弱いです。例えば、数字でない文字列 (“abc”) や空文字列 (“”) を渡した場合、多くの場合 0 を返しますが、これが本当に “0” という文字列だったのか、変換エラーなのか区別できません。
  • 変換できる数値の範囲を超えた場合(オーバーフロー)の動作も未定義です。
  • より安全に変換を行いたい場合は、後述する strtol 関数を使うのが推奨されます。

2. 文字列を浮動小数点数に変換: atof()

整数だけでなく、小数を含む数値を扱いたい場合もありますね。atof 関数は、文字列を double 型の浮動小数点数に変換します。”ASCII to float” の略ですが、戻り値は double 型である点に注意してください。こちらも <stdlib.h> ヘッダーファイルが必要です。

atof() 関数の使い方

#include <stdlib.h> // atof を使うために必要

double atof(const char *str);
  • 引数 str: 変換したい文字列へのポインタ。
  • 戻り値: 変換された double 型の数値。変換できない場合やエラーの場合、通常 0.0 が返りますが、これも保証されません。

コード例

#include <stdio.h>
#include <stdlib.h>

int main() {
    char str_double[] = "3.14159";
    double pi_approx = atof(str_double);

    printf("文字列 \"%s\" を double に変換すると %f です。\n", str_double, pi_approx);

    char str_exp[] = "1.23e-4"; // 指数表記もOK
    double exp_val = atof(str_exp);
    printf("文字列 \"%s\" を double に変換すると %g です。\n", str_exp, exp_val);

    char str_invalid[] = "hello";
    double invalid_double = atof(str_invalid);
    printf("文字列 \"%s\" を変換しようとすると %f になります。\n", str_invalid, invalid_double); // 通常 0.0

    return 0;
}
注意点:
  • atofatoi と同様に、エラーチェック機能が弱いです。変換失敗と 0.0 の区別がつきません。
  • オーバーフローやアンダーフロー(非常に小さい数)の場合の動作も環境依存です。
  • より堅牢な変換には、後述の strtod 関数が推奨されます。

atoiatof は手軽ですが、エラー処理が不十分です。より信頼性の高いプログラムを作るためには、strtol (string to long) や strtod (string to double) を使うことを検討しましょう。これらはエラーの詳細な情報を提供してくれます。

関数 ヘッダー 変換先 主な利点
strtol <stdlib.h> long int 変換不能文字の位置取得、基数指定、オーバーフロー検出
strtod <stdlib.h> double 変換不能文字の位置取得、オーバーフロー/アンダーフロー検出
strtof <stdlib.h> float strtodfloat
strtold <stdlib.h> long double strtodlong double

これらの関数は少し使い方が複雑になりますが、エラー発生時にも適切に対応できるため、より安全なコードになります。本格的な開発ではこちらを使うことが多いです。👍

(ここでは詳細な使い方は省略しますが、興味がある方はぜひ調べてみてください!)

3. 乱数を生成する: rand() と srand()

ゲームで敵の動きをランダムにしたり、シミュレーションで偶然の要素を入れたりしたい場合、乱数が必要になります。C言語では rand 関数を使って「擬似乱数」を生成できます。

擬似乱数とは、完全にランダムではなく、ある計算式に基づいて生成される数列のことです。しかし、実用上はランダムな数として扱える場合が多いです。rand 関数も <stdlib.h> ヘッダーファイルで定義されています。

rand() 関数の使い方

#include <stdlib.h> // rand, srand を使うために必要

int rand(void);
  • 引数: なし。
  • 戻り値: 0 から RAND_MAX までの範囲の int 型の擬似乱数。RAND_MAX<stdlib.h> で定義されている定数で、環境によって値が異なります(少なくとも 32767 以上)。

単純な rand() の使用例

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("RAND_MAX の値は %d です。\n", RAND_MAX);

    printf("乱数を 5 つ生成します:\n");
    for (int i = 0; i < 5; ++i) {
        printf("%d\n", rand());
    }

    return 0;
}

このコードを実行すると、5つの乱数が表示されます。しかし、何度実行しても毎回同じ順番で同じ数値が表示されるはずです。これは、擬似乱数生成の「種(seed)」が初期化されていないため、常に同じ数列が生成されるからです。😲

乱数の種を設定する: srand()

毎回違う乱数列を生成するには、プログラムを実行するたびに異なる「種」を乱数生成器に与える必要があります。そのために使うのが srand 関数です。

srand() 関数の使い方

#include <stdlib.h>

void srand(unsigned int seed);
  • 引数 seed: 乱数生成の種となる unsigned int 型の数値。
  • 戻り値: なし。

srand に与える種は、プログラム実行ごとに変化する値を使うのが一般的です。よく使われるのは、現在の時刻です。<time.h> ヘッダーの time 関数を使うと、現在時刻を秒単位で取得できます(通常、1970年1月1日からの経過秒数)。

time(NULL) は、現在の時刻を表す数値を返すため、これを srand の種として使うことで、プログラムを実行するたびに異なる乱数列を得られる可能性が高くなります。

srand() と time() を使ったコード例

#include <stdio.h>
#include <stdlib.h>
#include <time.h> // time() を使うために必要

int main() {
    // 現在時刻を種にして乱数生成器を初期化
    // srand() はプログラム中で通常1回だけ呼び出す
    srand((unsigned int)time(NULL));

    printf("srand() で初期化した後の乱数を 5 つ生成します:\n");
    for (int i = 0; i < 5; ++i) {
        printf("%d\n", rand());
    }

    return 0;
}

このコードを実行すると、実行するたびに異なる乱数列が表示されるはずです。(ただし、1秒以内に連続して実行すると同じ種になる可能性があります。)

特定の範囲の乱数を生成する

rand()0 から RAND_MAX までの数を返しますが、例えば「1から6までのサイコロの目🎲」のように、特定の範囲の乱数が欲しい場合が多いです。これは剰余演算子 % を使って実現できます。

  • rand() % N : 0 から N-1 までの範囲の乱数を生成します。
  • rand() % N + 1 : 1 から N までの範囲の乱数を生成します。

コード例(1から6のサイコロ)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    srand((unsigned int)time(NULL));

    printf("サイコロを 5 回振ります:\n");
    for (int i = 0; i < 5; ++i) {
        int dice = rand() % 6 + 1; // 0-5 の範囲にしてから +1 で 1-6 にする
        printf("%d回目: %d\n", i + 1, dice);
    }

    return 0;
}
rand() の注意点:
  • rand() が生成する擬似乱数は、統計的な質があまり高くないことが知られています。特に下位ビットには偏りが出やすいとされています。暗号化など、高品質な乱数が必要な用途には向きません。
  • rand() % N で範囲を限定する方法も、NRAND_MAX に比べて小さい場合、完全に均等な分布にならない可能性があります。
  • より高品質な乱数が必要な場合は、C++の <random> ライブラリ(C言語標準ではありません)や、OSが提供する乱数生成機能(例: Linuxの /dev/urandom)などを利用することになりますが、これらは少し高度なトピックです。

まとめ ✨

今回は、C言語の標準ライブラリに含まれる便利な関数として、以下を学びました。

  • atoi(): 文字列を int 型に変換する(エラー処理は弱い)。
  • atof(): 文字列を double 型に変換する(エラー処理は弱い)。
  • (参考) strtol(), strtod(): より安全な数値変換関数。
  • rand(): 0 から RAND_MAX までの擬似乱数を生成する。
  • srand(): 乱数生成の種を設定する。time(NULL) と組み合わせて使うことが多い。

これらの関数は、ユーザー入力の処理や、プログラムにランダム性を持たせるために非常に役立ちます。特に rand() は、簡単なゲームやシミュレーションを作る際に活躍するでしょう。

ただし、atoi, atof, rand にはそれぞれ注意点もあります。より堅牢なプログラムや高品質な乱数が必要な場合は、代替手段があることも覚えておきましょう。

次のステップでは、プログラムが必要に応じてメモリを確保・解放する「動的メモリ管理」について学んでいきます。お楽しみに!💪

参考情報 📚

コメント

タイトルとURLをコピーしました