ポインタ⑧(高度な使用法) 解答ページ | Programming Place Plus C言語編 第38章

トップページC言語編第38章

問題①

問題① 関数ポインタのところで登場した、難易度に応じて処理を分岐させるサンプルプログラムに、結果を出力するような関数を追加してください。 この関数は、引数で得点を受け取り、難易度ごとに異なる方法で合格・不合格を決定するものとします。 たとえば、簡単なモードなら 60点で合格ですが、難しいと 80点必要という感じです。 得点は、main_proc_XXXX関数が戻り値で返すようにするなど、元のプログラムを一部修正して構いません。
なお、関数ポインタを使う方法と、使わない方法の両方で、プログラムを作成してください。


まず、関数ポインタを使わない方法の解答例です。

#include <stdio.h>
#include <assert.h>

int main_proc_for_easy(void);
int main_proc_for_normal(void);
int main_proc_for_hard(void);
void result_proc_for_easy(int score);
void result_proc_for_normal(int score);
void result_proc_for_hard(int score);

int main(void)
{
    puts("難易度を選んでください。");
    puts("0~2 で大きいほど難しくなります。");

    char str[40];
    int level;
    fgets(str, sizeof(str), stdin);
    sscanf(str, "%d", &level);

    int score;

    // 難易度に応じたメインの処理を実行する
    switch (level) {
    case 0:
        score = main_proc_for_easy();
        break;
    case 1:
        score = main_proc_for_normal();
        break;
    case 2:
        score = main_proc_for_hard();
        break;
    default:
        puts("入力が正しくありません。");
        return 0;
    }

    // 難易度に応じた合否判定
    switch (level) {
    case 0:
        result_proc_for_easy(score);
        break;
    case 1:
        result_proc_for_normal(score);
        break;
    case 2:
        result_proc_for_hard(score);
        break;
    default:
        assert(0);
        return 0;
    }
}

int main_proc_for_easy(void)
{
    puts("簡単なモードで実行。");
    return 65;
}

int main_proc_for_normal(void)
{
    puts("標準的な難易度のモードで実行。");
    return 65;
}

int main_proc_for_hard(void)
{
    puts("難しいモードで実行。");
    return 65;
}

void result_proc_for_easy(int score)
{
    if (score >= 60) {
        puts("合格");
    }
    else {
        puts("不合格");
    }
}

void result_proc_for_normal(int score)
{
    if (score >= 70) {
        puts("合格");
    }
    else {
        puts("不合格");
    }
}

void result_proc_for_hard(int score)
{
    if (score >= 80) {
        puts("合格");
    }
    else {
        puts("不合格");
    }
}

実行結果:

難易度を選んでください。
0~2 で大きいほど難しくなります。
1
標準的な難易度のモードで実行。
不合格

main_proc_XXXX関数の戻り値を int型に変更し、得点を返すようにしてあります。 返す得点は定数にしていますが、もちろん本当のプログラムなら、正しい得点を返すようにします。

関数ポインタを使わないこの方法の場合、分岐構造が2か所に必要になります。 このように、どんどん同じ趣旨の分岐構造がプログラム中にばら撒かれていく傾向があります。

次に、関数ポインタを使った方法です。

#include <stdio.h>

#define SIZE_OF_ARRAY(array)    (sizeof(array)/sizeof(array[0]))

int main_proc_for_easy(void);
int main_proc_for_normal(void);
int main_proc_for_hard(void);
void result_proc_for_easy(int score);
void result_proc_for_normal(int score);
void result_proc_for_hard(int score);

int main(void)
{
    typedef int (*main_proc_t)(void);  // メイン処理の関数ポインタ型
    typedef void (*result_proc_t)(int);  // 結果処理の関数ポインタ型

    const main_proc_t main_proc_Array[] = {  // メイン処理の関数テーブル
        main_proc_for_easy,
        main_proc_for_normal,
        main_proc_for_hard,
    };
    const result_proc_t result_proc_array[] = {  // 結果処理の関数テーブル
        result_proc_for_easy,
        result_proc_for_normal,
        result_proc_for_hard,
    };


    puts("難易度を選んでください。");
    puts("0~2 で大きいほど難しくなります。");

    char str[40];
    int level;
    fgets(str, sizeof(str), stdin);
    sscanf(str, "%d", &level);

    if (level < 0 || SIZE_OF_ARRAY(main_proc_Array) <= level) {
        puts("入力が正しくありません。");
        return 0;
    }

    // 難易度に応じたメインの処理を実行する
    int score = main_proc_Array[level]();

    // 難易度に応じた合否判定
    result_proc_array[level](score);
}

int main_proc_for_easy(void)
{
    puts("簡単なモードで実行。");
    return 65;
}

int main_proc_for_normal(void)
{
    puts("標準的な難易度のモードで実行。");
    return 65;
}

int main_proc_for_hard(void)
{
    puts("難しいモードで実行。");
    return 65;
}

void result_proc_for_easy(int score)
{
    if (score >= 60) {
        puts("合格");
    }
    else {
        puts("不合格");
    }
}

void result_proc_for_normal(int score)
{
    if (score >= 70) {
        puts("合格");
    }
    else {
        puts("不合格");
    }
}

void result_proc_for_hard(int score)
{
    if (score >= 80) {
        puts("合格");
    }
    else {
        puts("不合格");
    }
}

実行結果:

難易度を選んでください。
0~2 で大きいほど難しくなります。
1
標準的な難易度のモードで実行。
不合格

この方法だと、分岐構造は増えません。新たな関数ポインタテーブルを用意し、添字を変えることで処理を分岐できます。

問題②

問題② qsort関数や bsearch関数に渡す関数ポインタにおいて、指し示す先の関数の引数が void*型ではなく、const void*型である理由はなぜでしょうか。


const void*型であれば、渡されたポインタを経由して、その先にある値を書き換えることができなくなります。この関数が呼び出されるのは、qsort関数や bsearch関数が、ソートやサーチの作業を行っている「途中」ですから、突然、値の一部が書き換えられると、正しく作業できなくなってしまいます。

const型修飾子のうまい使い方はこういうものです。コメントやドキュメントで「書き換えないでください」と書いても何の強制力も働きませんが、const型修飾子を明示すれば、強制力が働きます。


参考リンク


更新履歴

≪さらに古い更新履歴を展開する≫



第38章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る