第7章の4 一般ユーティリティ関数

1. 代表的な一般ユーティリティ関数

C言語の標準ライブラリには、多くの便利なユーティリティ関数が用意されており、これらを活用することでプログラムを効率的に記述できます。
#include <stdlib.h> が必要です。「#include」については「18-2」 を参照してください。

【一般ユーティリティ関数】
関数 説明 使用例
atoi(str) 文字列str を int型変数に変換して返す。
int i;
char str[] = "123";

i = atoi(str);
・・・ i は整数値の 123
atof(str) 文字列str を double型変数に変換して返す。
double d;
char str[] = "123.456";

d = atof(str);
・・・ d は浮動小数点数の 123.456
atol(str) 文字列str を long型変数に変換して返す。
long l;
char str[] = "123456";

l = atol(str);
・・・ l は整数値の 123456
rand() 整数の擬似乱数(関数コールするたびに異なる値)を返す。
int d;

d = rand();
・・・ d は整数の擬似乱数
system(cmnd) コマンドプロセッサを呼び出し、cmnd で指定したコマンドを実行する。
system("dir");・・・dirコマンドを実行
exit(status)

任意の場所でプログラムを終了させる。

status は、
 成功終了:0 又は EXIT_SUCCESS
 失敗終了:EXIT_FAILUREを用いる。

成功終了 ==> exit(EXIT_SUCCESS);

失敗終了 ==> exit(EXIT_FAILURE);

2. 疑似乱数の発生

乱数とは、要は「でたらめな値」ですが、コンピュータ内ではある規則に従って「でたらめな値」を生成しています。これを擬似乱数と言います。

(1) 疑似乱数を返却するrand関数

rand関数は、0~RAND_MAX の間の疑似乱数を返します。

rand を使って発生させた擬似乱数は srand関数 で乱数の発生系列を変更しない限り、実行のたびに同じ擬似乱数を発生します。下の使用例のプログラムを実行すると、実行のたびに同じ繰返しで擬似乱数を発生します。

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

int main(void)
{
    // 1~10の擬似乱数を10個発生
    for (int i = 1; i <= 10; i++) {
        printf("%2d ",rand() % 10 + 1);
    }
    printf("\n");

    return 0;
}

【実行結果例】
2 8 5 1 10 5 9 9 3 5

※ 環境により異なります。

  • RAND_MAXの値は、処理系により異なりますが32767以上と決められています。
  • 限定された範囲の疑似乱数を求めるには一般的に%演算子を利用します。
int r1 = rand();             // 0~RAND_MAXの疑似乱数
int r2 = rand() % 3; 	     // 3で割った余り(0~2)
int r3 = rand() % 3 + 1;     // 3で割った余り(0~2)+ 1は1~3
int r4 = rand() % 11 + 10;   // 11で割った余り(0~10)+ 10 は10 ~ 20
double r5 = (double)rand() / RAND_MAX; 	// 0.0~1.0以下の擬似乱数

(2) 擬似乱数の発生系列を変更するsrand関数

srand関数は rand関数で発生させる擬似乱数の発生系列を変更します。srand関数の引数seedに同じ数値を与えると、rand関数は同じ繰返しで擬似乱数を発生させます。

実行のたびに異なるパターンで疑似乱数を発生させるには、time関数を使って現在時刻を取得し、それをシードにしてsrandを呼び出すのが一般的です。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>    // time関数を使用するために必要

int main(void)
{
    // 乱数系列の変更
    srand((unsigned) time(NULL));

    // 1~10の擬似乱数を10個発生
    for (int i = 1; i <= 10; i++) {
        printf("%2d ",rand() % 10 + 1);
    }
    printf("\n");

    return 0;
}

【実行結果例】
4 5 2 5 9 10 6 10 3 9

※ 環境により異なります。

3. scanf()で読み込み不一致が起こる問題を回避する

scanf()で読み込み不一致が起こる問題では、上記 「atoi()、atol()、atof()」を使って回避します。

読み込み不一致は、scanf()を for や while などのループの中で用いた場合、入力バッファに残されたデータを永遠に受け付けないため無限ループになってしまい意外に深刻な問題となります。

そのためデータを一旦、scanf(“%s”, ○)や fgets()を用いて文字列として入力し、それから上記の変換関数を用いて期待するデータ型に変換するのです。

// 繰り返し整数値を入力する例
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    int i = 0;
    char a[20];

    do {
        printf("整数値を入力してください(終了条件:-1)> ");
        scanf("%19s", a);
        i = atoi(a);
        printf("%dを入力しました。\n", i);
    } while(i != -1);
    printf("終了しました。\n");

    return 0;
}

【実行結果例】
整数値を入力してください(終了条件:-1)> 1
1を入力しました。
整数値を入力してください(終了条件:-1)> 2
2を入力しました。
整数値を入力してください(終了条件:-1)> A ← 整数値以外
0を入力しました。
整数値を入力してください(終了条件:-1)> -1
-1を入力しました。
終了しました。

水色文字はキーボードからの入力

〇 演習問題

問1

+ をランダムな個数ずつ、10行画面に出力しなさい。ただし、最大でも 1行(80個)を越えないこと。

【実行結果例】
+++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++
++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

※ 環境によりことなります。

問2

キーボードから数値になりうる文字列を入力し、 その文字列が整数値に該当するなら、int型の整数値に変換して表示しなさい。
また、浮動小数点数に該当するなら、double型の浮動小数点数に変換して表示しなさい。
整数値にも浮動小数点数にも該当しないなら「入力エラー」と表示しなさい。

【実行結果例1】(整数値に該当する文字列を入力)
数値を入力しなさい > -0012345678
整数値入力 = -12345678

【実行結果例2】(浮動小数点数に該当する文字列を入力)
数値を入力しなさい > 9876.543
浮動小数点数入力 = 9876.543000

【実行結果例3】(数値に該当しない文字列を入力)
数値を入力しなさい > 123.456.789
入力エラー

水色文字はキーボードからの入力

解答例

// 問1
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    for (int i = 0; i < 10; i++) {
        int r = rand() % 80;
        for (int j = 0; j < r; j++) {
            putchar('+');
        }
        putchar('\n');
    }

    return 0;
}
// 問2
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main(void)
{
    char str[100];
    int i, dot = 0, err;
    int n;
    double d;

    printf("数値を入力しなさい > ");
    scanf("%99s", str);

    // 先頭文字の判定
    if (str[0] == '+' || str[0] == '-' || isdigit(str[0]) != 0) {
        err = 0;
    }
    else if (str[0] == '.') {
        err = 0;
        dot++;
    }
    else {
        err = 1;
    }

    // 2文字目以降の判定
    i = 1;
    while (err == 0 && str[i] != '\0') {
        if (str[i] == '.') {
            dot++;
        }
        else if (isdigit(str[i]) == 0) {
            err++;
        }
        i++;
    }

    // 上記判定でエラーでない場合
    if (err == 0) {
        if (dot == 0) {              // 整数
            n = atoi(str);
            printf("整数値入力 = %d\n", n);
        }
        else if (dot == 1) {         // 浮動小数点数
            d = atof(str);
            printf("浮動小数点数入力 = %f\n", d);
        }
        else {                       // 小数点が複数ある場合
            printf("入力エラー\n");
        }
    }
    else {
        printf("入力エラー\n");
    }

    return 0;
}

コメント