広告スペース

C言語 練習問題集

大学課題レベルの実践問題を全39問収録。難易度フィルター・ヒント・解答コード付きで自主学習・試験対策に最適です。

条件分岐の問題

EX-01 初級
クラス分け判定
学生番号(正の整数)を入力すると、クラスの割り振りを行うプログラムを作成せよ。
・番号が1〜20 → Aクラス
・番号が21以上で偶数 → Aクラス
・番号が21以上で奇数 → Bクラス
学生番号を入力: 25 学生番号25はBクラスです
if文で番号の範囲を判定します。21以上の場合は % 演算子で偶数・奇数を判定しましょう。
偶数: n % 2 == 0、奇数: n % 2 != 0
#include <stdio.h>

int main(void) {
    int num;
    printf("学生番号を入力: ");
    scanf("%d", &num);

    if (num >= 1 && num <= 20) {
        printf("学生番号%dはAクラスです\n", num);
    } else if (num % 2 == 0) {
        printf("学生番号%dはAクラスです\n", num);
    } else {
        printf("学生番号%dはBクラスです\n", num);
    }
    return 0;
}
EX-02 初級
3つの整数の最大値
キーボードから整数を3つ(すべて異なる値)入力し、入力順に表示した後、最も大きな数を表示するプログラムを作成せよ。
if-else if-else文と論理演算子を使うこと。
a = 12 b = 45 c = 7 入力値: a=12, b=45, c=7 最大値は 45 です
a >= b && a >= c のようにAND演算子で組み合わせて最大値を判定します。
#include <stdio.h>

int main(void) {
    int a, b, c;
    printf("a = "); scanf("%d", &a);
    printf("b = "); scanf("%d", &b);
    printf("c = "); scanf("%d", &c);
    printf("入力値: a=%d, b=%d, c=%d\n", a, b, c);

    if (a >= b && a >= c) {
        printf("最大値は %d です\n", a);
    } else if (b >= a && b >= c) {
        printf("最大値は %d です\n", b);
    } else {
        printf("最大値は %d です\n", c);
    }
    return 0;
}
EX-03 初級
偶数奇数判定と商・余り
2つの整数を入力して変数 a, b に格納し、それぞれの偶数・奇数を表示した後、a÷bの商と余りを表示するプログラムを作成せよ。
a = 17 b = 5 aは奇数です bは奇数です 17 / 5 = 商3, 余り2
商: a / b(整数除算)、余り: a % b。偶数奇数は n % 2 で判定。
#include <stdio.h>

int main(void) {
    int a, b;
    printf("a = "); scanf("%d", &a);
    printf("b = "); scanf("%d", &b);
    printf("aは%sです\n", a % 2 == 0 ? "偶数" : "奇数");
    printf("bは%sです\n", b % 2 == 0 ? "偶数" : "奇数");
    printf("%d / %d = 商%d, 余り%d\n", a, b, a / b, a % b);
    return 0;
}

ループ・配列の問題

EX-04 初級
九九の表
1行目から9行目まで、二重forループを使って九九の表を表示するプログラムを作成せよ。外側のループは1つの for 文で記述すること。
1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 ... 9 18 27 36 45 54 63 72 81
外側 for(i=1; i<=9; i++)、内側 for(j=1; j<=9; j++)。桁揃えに %2d%3d を使います。
#include <stdio.h>

int main(void) {
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= 9; j++) {
            printf("%3d", i * j);
        }
        printf("\n");
    }
    return 0;
}
EX-05 初級
配列の最小値を探す
5要素の int 型配列 {88, 72, 95, 55, 63} の中から最小の値とそのインデックスを見つけて表示するプログラムを作成せよ。
minimum is d[3]: 55
最小値のインデックスを保持する変数 min_idx を用意し、ループで比較していきます。初期値は 0 にします。
#include <stdio.h>

int main(void) {
    int d[] = {88, 72, 95, 55, 63};
    int n = 5, min_idx = 0;

    for (int i = 1; i < n; i++) {
        if (d[i] < d[min_idx]) min_idx = i;
    }
    printf("minimum is d[%d]: %d\n", min_idx, d[min_idx]);
    return 0;
}
EX-06 中級
文字列の逆順表示
キーボードから入力された文字列(空白なし)を逆順に表示するプログラムを作成せよ。
ヒント: 文字列の最後は '\0'
文字列を入力してください: abcdef 文字列の逆順は fedcba です
strlen(str) で長さを取得し、最後の文字(index = len-1)から先頭(index = 0)に向かって1文字ずつ表示します。
#include <stdio.h>
#include <string.h>

int main(void) {
    char str[128];
    printf("文字列を入力してください: ");
    scanf("%s", str);

    int len = strlen(str);
    printf("文字列の逆順は ");
    for (int i = len - 1; i >= 0; i--) {
        printf("%c", str[i]);
    }
    printf(" です\n");
    return 0;
}
EX-07 中級
四角パターン描画
サイズ(幅と高さ)を入力すると、* で四角の枠を描き、対角線の位置に + を表示するプログラムを作成せよ。内部は空白にする。
幅: 7 高さ: 5 ******* *+ +* * + + * * + * *******
二重ループで各マス目の文字を決定します。枠線(i==0, i==h-1, j==0, j==w-1)、対角線(i==j または i+j==w-1)で場合分けします。
#include <stdio.h>

int main(void) {
    int w, h;
    printf("幅: "); scanf("%d", &w);
    printf("高さ: "); scanf("%d", &h);

    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            if (i == 0 || i == h-1 || j == 0 || j == w-1)
                printf("*");
            else if (i == j || i + j == w - 1)
                printf("+");
            else
                printf(" ");
        }
        printf("\n");
    }
    return 0;
}

関数の問題

EX-08 初級
顔文字表示関数
disp_face 関数を作成し、呼び出すたびに (^_^) を表示するプログラムを作成せよ。main関数で3回呼び出し、(^_^)(^_^)(^_^) と表示する。
(^_^)(^_^)(^_^)
引数なし・戻り値なしの void disp_face(void) を定義し、中で printf("(^_^)") するだけです。
#include <stdio.h>

void disp_face(void) {
    printf("(^_^)");
}

int main(void) {
    disp_face();
    disp_face();
    disp_face();
    printf("\n");
    return 0;
}
EX-09 中級
長方形の面積関数(プロトタイプ宣言)
長方形の面積を求める area_rectangle 関数を関数プロトタイプ宣言を用いて定義せよ。
・main関数でキーボードから「たて」と「よこ」を入力
・関数は面積の計算のみを行い、結果をreturnする
・main関数で面積を表示する
たて: 5 よこ: 8 面積は 40 です
プロトタイプ宣言: int area_rectangle(int h, int w); を main の前に書きます。関数本体は main の後に書きます。
#include <stdio.h>

int area_rectangle(int h, int w); // プロトタイプ宣言

int main(void) {
    int h, w;
    printf("たて: "); scanf("%d", &h);
    printf("よこ: "); scanf("%d", &w);
    printf("面積は %d です\n", area_rectangle(h, w));
    return 0;
}

int area_rectangle(int h, int w) {
    return h * w;
}
EX-10 中級
最小公倍数を求める関数
2つの整数を入力し、最小公倍数(LCM)を計算する関数 lcm を作成せよ。
ユークリッドの互除法で最大公約数(GCD)を求め、LCM = a * b / GCD で計算する。
a = 12 b = 18 最小公倍数は 36 です
ユークリッドの互除法: while(b != 0) { tmp = b; b = a % b; a = tmp; } 最後の a が GCD。
LCM = 元の a × 元の b / GCD
#include <stdio.h>

int gcd(int a, int b) {
    while (b != 0) {
        int tmp = b;
        b = a % b;
        a = tmp;
    }
    return a;
}

int lcm(int a, int b) {
    return a / gcd(a, b) * b;
}

int main(void) {
    int a, b;
    printf("a = "); scanf("%d", &a);
    printf("b = "); scanf("%d", &b);
    printf("最小公倍数は %d です\n", lcm(a, b));
    return 0;
}
EX-11 中級
マクロの活用
以下の条件を満たすプログラムを作成せよ。
VALUE を 50 としてマクロ定義
・2つの値を足すマクロ関数 SUM(a,b) を定義
・main関数では printf と return のみ書き、50+50=100 と表示する
50+50=100
#define VALUE 50#define SUM(a,b) ((a)+(b)) を定義します。マクロ関数では引数をカッコで囲むのが安全です。
#include <stdio.h>
#define VALUE 50
#define SUM(a, b) ((a) + (b))

int main(void) {
    printf("%d+%d=%d\n", VALUE, VALUE, SUM(VALUE, VALUE));
    return 0;
}

総合問題

EX-12 上級
昇順ソート関数
キーボードから整数を5個入力し、昇順(小さい順)にソートして表示するプログラムを作成せよ。
ソート部分は void ascend(int arr[], int n) 関数として実装すること。バブルソートを使う。
入力1: 10 入力2: 32 入力3: 8 入力4: 42 入力5: 13 昇順ソート: 8, 10, 13, 32, 42
バブルソート: 配列の後ろから前に向かって隣接要素を比較し、大小が逆なら交換。これを要素数-1回繰り返す。
交換: tmp = a[j-1]; a[j-1] = a[j]; a[j] = tmp;
#include <stdio.h>

void ascend(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = n - 1; j > i; j--) {
            if (arr[j - 1] > arr[j]) {
                int tmp = arr[j - 1];
                arr[j - 1] = arr[j];
                arr[j] = tmp;
            }
        }
    }
    printf("昇順ソート: ");
    for (int i = 0; i < n; i++) {
        printf("%d", arr[i]);
        if (i < n - 1) printf(", ");
    }
    printf("\n");
}

int main(void) {
    int data[5];
    for (int i = 0; i < 5; i++) {
        printf("入力%d: ", i + 1);
        scanf("%d", &data[i]);
    }
    ascend(data, 5);
    return 0;
}
EX-13 上級
グローバル変数を使った電卓
以下の仕様でプログラムを作成せよ。
・グローバル変数 val(初期値0)と n を宣言
input(): キーボードから整数を入力し n に代入
addition(): val に n を加算
product(): val を n 倍
display(): val の値を表示
・main関数で 1:加算 2:乗算 -1:終了 のメニューをwhileループで実装
1: 加算 2:乗算 -1:終了> 1 値を入力してください> 10 valの値は10になりました 1: 加算 2:乗算 -1:終了> 2 値を入力してください> 3 valの値は30になりました 1: 加算 2:乗算 -1:終了> -1
while(sw != -1) でループし、switch(sw) で分岐。各case内で input → addition/product → display の順に呼び出します。
#include <stdio.h>

int val = 0;
int n;

void input(void)    { printf("値を入力してください> "); scanf("%d", &n); }
void display(void)  { printf("valの値は%dになりました\n", val); }
void addition(void) { val += n; }
void product(void)  { val *= n; }

int main(void) {
    int sw = 0;
    while (sw != -1) {
        printf("1: 加算 2:乗算 -1:終了> ");
        scanf("%d", &sw);
        switch (sw) {
        case 1: input(); addition(); display(); break;
        case 2: input(); product();  display(); break;
        }
    }
    return 0;
}
EX-14 上級
グラフ描画関数
商品の販売数を配列に格納し、draw_graph 関数で棒グラフを描画するプログラムを作成せよ。
・グローバル変数で目盛り間隔を設定(例: 5)
・販売数分の * を表示し、目盛り位置には = を表示
・プロトタイプ宣言を行うこと
商品1 (12): ****=****=** 商品2 ( 8): ****=*** 商品3 (20): ****=****=****=****=
ループで1文字ずつ表示。j % interval == 0 && j != 0 なら =、それ以外は * を表示します。
#include <stdio.h>

void draw_graph(int id, int sales);
int interval = 5; // グローバル: 目盛り間隔

int main(void) {
    int data[] = {12, 8, 20};
    int n = 3;
    for (int i = 0; i < n; i++)
        draw_graph(i + 1, data[i]);
    return 0;
}

void draw_graph(int id, int sales) {
    printf("商品%d (%2d): ", id, sales);
    for (int j = 1; j <= sales; j++) {
        if (j % interval == 0)
            printf("=");
        else
            printf("*");
    }
    printf("\n");
}

printf・scanf・書式の問題

EX-15 初級
重さの書式付き表示
2つの重さ(実数)を入力して、小数点以下1桁まで揃えて表示するプログラムを作成せよ。合計と差も表示する。
重さ1: 3.5 重さ2: 7.25 重さ1 = 3.5 kg 重さ2 = 7.3 kg 合計 = 10.8 kg 差 = 3.8 kg
printf("%5.1f", val) で全体5桁・小数点以下1桁の右寄せ表示ができます。
#include <stdio.h>

int main(void) {
    double w1, w2;
    printf("重さ1: "); scanf("%lf", &w1);
    printf("重さ2: "); scanf("%lf", &w2);
    printf("重さ1 = %5.1f kg\n", w1);
    printf("重さ2 = %5.1f kg\n", w2);
    printf("合計  = %5.1f kg\n", w1 + w2);
    printf("差    = %5.1f kg\n", w1 > w2 ? w1 - w2 : w2 - w1);
    return 0;
}
EX-16 中級
3変数の数式計算
キーボードから実数 x, y, z を入力し、f(x,y,z) = x*x + 2*x*y + y*y*z の値を計算して表示するプログラムを作成せよ。
x: 1.2 y: 2.2 z: 1.5 f(x,y,z) = 13.920000
double型で変数を宣言し、scanf("%lf", &x) で読み込みます。計算式をそのまま書けばOKです。
#include <stdio.h>

int main(void) {
    double x, y, z;
    printf("x: "); scanf("%lf", &x);
    printf("y: "); scanf("%lf", &y);
    printf("z: "); scanf("%lf", &z);
    double result = x*x + 2*x*y + y*y*z;
    printf("f(x,y,z) = %f\n", result);
    return 0;
}

ポインタの問題

EX-17 中級
ポインタで値を交換
2つの整数を入力し、ポインタを引数に取る swap 関数で値を交換して表示するプログラムを作成せよ。
a = 10 b = 20 交換前: a=10, b=20 交換後: a=20, b=10
関数の引数を int *pa, int *pb にし、一時変数 tmp を使って *pa*pb を交換します。呼び出し側は swap(&a, &b)
#include <stdio.h>

void swap(int *pa, int *pb) {
    int tmp = *pa;
    *pa = *pb;
    *pb = tmp;
}

int main(void) {
    int a, b;
    printf("a = "); scanf("%d", &a);
    printf("b = "); scanf("%d", &b);
    printf("交換前: a=%d, b=%d\n", a, b);
    swap(&a, &b);
    printf("交換後: a=%d, b=%d\n", a, b);
    return 0;
}
EX-18 中級
配列の合計・平均をポインタで計算
5個の整数を配列に格納し、ポインタを使って合計と平均を計算する関数を作成せよ。
関数 void calc(int *arr, int n, int *sum, double *avg) として実装する。
data = {10, 20, 30, 40, 50} 合計: 150 平均: 30.0
合計と平均をポインタ経由で返します。*sum = 0; で初期化し、ループで *sum += arr[i]; 、最後に *avg = (double)*sum / n;
#include <stdio.h>

void calc(int *arr, int n, int *sum, double *avg) {
    *sum = 0;
    for (int i = 0; i < n; i++) *sum += arr[i];
    *avg = (double)*sum / n;
}

int main(void) {
    int data[] = {10, 20, 30, 40, 50};
    int sum; double avg;
    calc(data, 5, &sum, &avg);
    printf("合計: %d\n", sum);
    printf("平均: %.1f\n", avg);
    return 0;
}
EX-19 上級
ポインタで文字列を大文字変換
文字列を受け取り、ポインタを使ってすべての小文字を大文字に変換する関数 to_upper(char *s) を作成せよ。
入力: Hello World 変換後: HELLO WORLD
'a' <= *s && *s <= 'z' なら *s -= 32;(ASCIIコードで大文字と小文字の差は32)。while(*s) でヌル終端まで走査。
空白を含む入力には fgets を使います。
#include <stdio.h>
#include <string.h>

void to_upper(char *s) {
    while (*s) {
        if (*s >= 'a' && *s <= 'z') *s -= 32;
        s++;
    }
}

int main(void) {
    char str[128];
    printf("入力: ");
    fgets(str, 128, stdin);
    str[strcspn(str, "\n")] = '\0';
    to_upper(str);
    printf("変換後: %s\n", str);
    return 0;
}

構造体の問題

EX-20 中級
学生の成績管理
名前(20文字以内)と点数を持つ構造体 Student を定義し、3人分のデータを入力して最高得点の学生を表示するプログラムを作成せよ。
学生1 名前: Taro 点数: 80 学生2 名前: Hanako 点数: 95 学生3 名前: Jiro 点数: 70 最高得点: Hanako (95点)
struct Student { char name[20]; int score; }; を定義し、配列で3人分確保。ループで最大スコアのインデックスを探します。
#include <stdio.h>

struct Student { char name[20]; int score; };

int main(void) {
    struct Student s[3];
    for (int i = 0; i < 3; i++) {
        printf("学生%d 名前: ", i+1); scanf("%s", s[i].name);
        printf("       点数: "); scanf("%d", &s[i].score);
    }
    int max = 0;
    for (int i = 1; i < 3; i++)
        if (s[i].score > s[max].score) max = i;
    printf("最高得点: %s (%d点)\n", s[max].name, s[max].score);
    return 0;
}
EX-21 上級
座標の距離計算
2次元座標 (x, y) を持つ構造体 Point を定義し、2点を入力して2点間の距離を計算する関数 double distance(Point p1, Point p2) を作成せよ。
点1 x: 0.0 y: 0.0 点2 x: 3.0 y: 4.0 距離: 5.000
距離 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))math.hsqrt を使います。コンパイル時に -lm オプションが必要な場合があります。
#include <stdio.h>
#include <math.h>

struct Point { double x, y; };

double distance(struct Point p1, struct Point p2) {
    double dx = p2.x - p1.x, dy = p2.y - p1.y;
    return sqrt(dx*dx + dy*dy);
}

int main(void) {
    struct Point p1, p2;
    printf("点1 x: "); scanf("%lf", &p1.x);
    printf("   y: "); scanf("%lf", &p1.y);
    printf("点2 x: "); scanf("%lf", &p2.x);
    printf("   y: "); scanf("%lf", &p2.y);
    printf("距離: %.3f\n", distance(p1, p2));
    return 0;
}

動的メモリ・ファイル入出力の問題

EX-22 上級
動的配列で任意個の整数を管理
最初に要素数 n を入力し、malloc で n 個分の配列を確保して n 個の整数を入力、合計と平均を表示するプログラムを作成せよ。最後に必ず free すること。
要素数: 4 data[0] = 10 data[1] = 20 data[2] = 30 data[3] = 40 合計: 100, 平均: 25.0
int *data = (int *)malloc(sizeof(int) * n); で確保。NULLチェックも忘れずに。使い終わったら free(data);
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n;
    printf("要素数: "); scanf("%d", &n);
    int *data = (int *)malloc(sizeof(int) * n);
    if (data == NULL) { printf("メモリ確保失敗\n"); return 1; }

    int sum = 0;
    for (int i = 0; i < n; i++) {
        printf("data[%d] = ", i); scanf("%d", &data[i]);
        sum += data[i];
    }
    printf("合計: %d, 平均: %.1f\n", sum, (double)sum / n);
    free(data);
    return 0;
}
EX-23 上級
ファイルに成績を書き出す
3人分の名前と点数を入力し、テキストファイル "scores.txt" に書き出すプログラムを作成せよ。書き出し後、ファイルを読み込んで内容を表示する。
名前1: Taro 点数: 80 名前2: Hanako 点数: 95 名前3: Jiro 点数: 70 --- scores.txt の内容 --- Taro 80 Hanako 95 Jiro 70
書き込み: fopen("scores.txt", "w")fprintffclose
読み込み: fopen("scores.txt", "r")fscanf を EOF まで繰り返す → fclose
#include <stdio.h>

int main(void) {
    char names[3][20]; int scores[3];
    for (int i = 0; i < 3; i++) {
        printf("名前%d: ", i+1); scanf("%s", names[i]);
        printf("  点数: "); scanf("%d", &scores[i]);
    }
    // 書き込み
    FILE *fp = fopen("scores.txt", "w");
    for (int i = 0; i < 3; i++)
        fprintf(fp, "%s %d\n", names[i], scores[i]);
    fclose(fp);
    // 読み込み
    printf("--- scores.txt の内容 ---\n");
    fp = fopen("scores.txt", "r");
    char n[20]; int s;
    while (fscanf(fp, "%s %d", n, &s) != EOF)
        printf("%s %d\n", n, s);
    fclose(fp);
    return 0;
}

パターン描画・アルゴリズムの問題

EX-24 中級
三角形パターン
高さ n を入力すると、* で直角三角形を描画するプログラムを作成せよ。
高さ: 5 * ** *** **** *****
外側ループで行(i=1〜n)、内側ループで * を i 個出力、最後に改行。
#include <stdio.h>

int main(void) {
    int n;
    printf("高さ: "); scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < i; j++) printf("*");
        printf("\n");
    }
    return 0;
}
EX-25 中級
グリッド描画(グローバル変数・関数)
幅、高さ、横間隔、縦間隔を入力すると、枠を #、目盛り位置を +、それ以外を - で表示する grid 関数を作成せよ。
間隔はグローバル変数で管理し、関数プロトタイプ宣言も行うこと。
幅: 10 高さ: 7 間隔横: 3 間隔縦: 2 ########## #--+--+--# #--------# #--+--+--# #--------# #--+--+--# ##########
枠: i==0 || i==h-1 || j==0 || j==w-1#
目盛り: i%v_interval==0 && j%h_interval==0+
それ以外: -
#include <stdio.h>

void grid(int w, int h);
int v_interval, h_interval;

int main(void) {
    int w, h;
    printf("幅: "); scanf("%d", &w);
    printf("高さ: "); scanf("%d", &h);
    printf("間隔横: "); scanf("%d", &h_interval);
    printf("間隔縦: "); scanf("%d", &v_interval);
    grid(w, h);
    return 0;
}

void grid(int w, int h) {
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            if (i==0||i==h-1||j==0||j==w-1) printf("#");
            else if (i%v_interval==0 && j%h_interval==0) printf("+");
            else printf("-");
        }
        printf("\n");
    }
}
EX-26 上級
素数判定
整数 n を入力し、2 から n までのすべての素数を表示するプログラムを作成せよ。素数判定は関数 int is_prime(int n) として実装する。
n = 30 2 3 5 7 11 13 17 19 23 29 素数の個数: 10
素数判定: 2 から sqrt(n) まで割り切れるかチェック。割り切れたら素数でない。math.hsqrt を使うと効率的。
#include <stdio.h>
#include <math.h>

int is_prime(int n) {
    if (n < 2) return 0;
    for (int i = 2; i <= (int)sqrt(n); i++)
        if (n % i == 0) return 0;
    return 1;
}

int main(void) {
    int n, count = 0;
    printf("n = "); scanf("%d", &n);
    for (int i = 2; i <= n; i++) {
        if (is_prime(i)) { printf("%d ", i); count++; }
    }
    printf("\n素数の個数: %d\n", count);
    return 0;
}
EX-27 上級
add関数とmax関数
以下の2つの関数を持つプログラムを作成せよ。
add(int a, int b): 2つの整数の和を表示
max(double a, double b, double c): 3つの実数の最大値を表示
main関数で int型変数(12, 25)とdouble型変数(23.5, 69.2, 10.3)を初期化して各関数を呼び出す。
12 + 25 = 37 max(23.5, 69.2, 10.3) = 69.2
add関数はvoidで中でprintfする方法と、intを返してmainでprintfする方法があります。max関数は3つの比較を行います。
#include <stdio.h>

void add(int a, int b) {
    printf("%d + %d = %d\n", a, b, a + b);
}

void max3(double a, double b, double c) {
    double m = a;
    if (b > m) m = b;
    if (c > m) m = c;
    printf("max(%.1f, %.1f, %.1f) = %.1f\n", a, b, c, m);
}

int main(void) {
    int x = 12, y = 25;
    double a = 23.5, b = 69.2, c = 10.3;
    add(x, y);
    max3(a, b, c);
    return 0;
}
EX-28 上級
降順ソート(選択ソート)
キーボードから整数を5個入力し、降順(大きい順)にソートして表示するプログラムを作成せよ。
EX-12はバブルソートだったが、今回は選択ソートで実装すること。
入力1: 5 入力2: 23 入力3: 11 入力4: 2 入力5: 17 降順ソート: 23, 17, 11, 5, 2
選択ソート: 未ソート部分から最大値を見つけて先頭と交換。降順なので「最大値」を探します。
#include <stdio.h>

void descend(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int max_idx = i;
        for (int j = i + 1; j < n; j++)
            if (arr[j] > arr[max_idx]) max_idx = j;
        int tmp = arr[i]; arr[i] = arr[max_idx]; arr[max_idx] = tmp;
    }
}

int main(void) {
    int data[5];
    for (int i = 0; i < 5; i++) {
        printf("入力%d: ", i+1); scanf("%d", &data[i]);
    }
    descend(data, 5);
    printf("降順ソート: ");
    for (int i = 0; i < 5; i++) {
        printf("%d", data[i]);
        if (i < 4) printf(", ");
    }
    printf("\n");
    return 0;
}
EX-29 上級
再帰で累乗計算
底 base と指数 exp を入力し、再帰関数 int power(int base, int exp) で累乗を計算するプログラムを作成せよ。ループは使わないこと。
底: 2 指数: 10 2^10 = 1024
ベースケース: exp == 0 → 1を返す。再帰: base * power(base, exp - 1)
#include <stdio.h>

int power(int base, int exp) {
    if (exp == 0) return 1;
    return base * power(base, exp - 1);
}

int main(void) {
    int b, e;
    printf("底: "); scanf("%d", &b);
    printf("指数: "); scanf("%d", &e);
    printf("%d^%d = %d\n", b, e, power(b, e));
    return 0;
}
EX-30 上級
二分探索
昇順にソート済みの配列から、入力された値を二分探索で探し、見つかったインデックスを表示するプログラムを作成せよ。見つからなければ「見つかりません」と表示する。
data = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91} 探す値: 23 data[5] に見つかりました
low=0, high=n-1 として、mid = (low + high) / 2 で中央を見る。arr[mid] == target なら発見、arr[mid] < target なら low = mid + 1、そうでなければ high = mid - 1
#include <stdio.h>

int binary_search(int arr[], int n, int target) {
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (arr[mid] == target) return mid;
        else if (arr[mid] < target) low = mid + 1;
        else high = mid - 1;
    }
    return -1;
}

int main(void) {
    int data[] = {2,5,8,12,16,23,38,56,72,91};
    int n = 10, target;
    printf("探す値: "); scanf("%d", &target);
    int idx = binary_search(data, n, target);
    if (idx >= 0) printf("data[%d] に見つかりました\n", idx);
    else printf("見つかりません\n");
    return 0;
}

穴埋め問題(期末試験対策)

以下の問題は、コードの 【???】 部分を埋めて完成させる形式です。実際の試験を意識した出題です。
EX-31 初級
母音判定プログラム(穴埋め)
キーボードから1文字入力し、母音(a,e,i,o,u)なら「母音です」、そうでなければ「子音です」と表示するプログラムの空欄を埋めよ。
一文字入力> e 「e」は母音です 一文字入力> k 「k」は子音です
#include <stdio.h>

int main(void) {
    char c;
    printf("一文字入力> ");
    scanf("%c", 【???】);

    if (c=='a' || c=='e' || c=='i' || 【???】 || c=='u') {
        printf("「%c」は母音です\n", c);
    } else {
        printf("「%c」は【???】です\n", c);
    }
    return 0;
}
1つ目: scanf でアドレスを渡すには? 2つ目: 残りの母音は? 3つ目: 母音でなければ何?
解答:
1つ目: &c(charのアドレスを渡す)
2つ目: c=='o'(残りの母音 o)
3つ目: 子音
EX-32 中級
四則演算電卓(穴埋め)
計算方法(1:加算 2:減算 3:乗算 4:除算)を選択し、2つの数値で計算するプログラムの空欄を埋めよ。不正な選択は再入力を求める。
計算方法(1:+ 2:- 3:* 4:/)> 4 x = 21.0 y = 4.0 x / y = 5.250000
#include <stdio.h>

int main(void) {
    int sw;
    double x, y;

    printf("計算方法(1:+ 2:- 3:* 4:/)> ");
    scanf("%d", &sw);
    while (【???】) {
        printf("1-4を入力> ");
        scanf("%d", &sw);
    }
    printf("x = "); scanf("【???】", &x);
    printf("y = "); scanf("%lf", &y);

    switch (【???】) {
    case 1: printf("x + y = %f\n", x + y); break;
    case 2: printf("x - y = %f\n", x - y); break;
    case 3: printf("x * y = %f\n", x * y); break;
    case 4: printf("x / y = %f\n", 【???】); break;
    }
    return 0;
}
while条件: 1〜4の範囲外を弾く。scanf書式: doubleの読み込み。switch: 何で分岐する? 除算の式: x/y。
解答:
1つ目: sw < 1 || sw > 4(範囲外なら繰り返す)
2つ目: %lf(doubleの読み込み書式)
3つ目: sw(switchで分岐する変数)
4つ目: x / y(除算の計算式)
EX-33 中級
文字列の長さ比較(穴埋め)
自作の length 関数を使い、2つの文字列の長さを比較するプログラムの空欄を埋めよ。
文字列1> orange 文字列2> apple orangeは6文字です appleは5文字です 合計は11文字です orangeの方が長いです
#include <stdio.h>
#define MAX 101

int length(char 【???】) {
    int len = 0;
    for (int i = 0; str[i] != 【???】; i++) {
        len++;
    }
    return 【???】;
}

int main(void) {
    char s1[MAX], s2[MAX];
    printf("文字列1> "); scanf("%s", s1);
    printf("文字列2> "); scanf("%s", s2);

    int len1 = length(s1), len2 = length(s2);
    printf("%sは%d文字です\n", s1, len1);
    printf("%sは%d文字です\n", s2, len2);
    printf("合計は%d文字です\n", 【???】);

    if (len1 > len2) printf("%sの方が長いです\n", s1);
    else if (len2 > len1) printf("%sの方が長いです\n", s2);
    else printf("同じ長さです\n");
    return 0;
}
関数引数: 配列を受け取る書き方は? 終端文字: C言語の文字列の末尾は? return: 何を返す? 合計: 2つの長さを足す。
解答:
1つ目: str[](配列を引数として受け取る)
2つ目: '\0'(ヌル終端文字)
3つ目: len(計算した文字数を返す)
4つ目: len1 + len2(合計文字数)
EX-34 上級
サイコロシミュレーション(穴埋め)
サイコロを n 回振り、各目の出現回数と割合を表示するプログラムの空欄を埋めよ。
回数: 1000 1の目: 168回 (16.8%) 2の目: 172回 (17.2%) 3の目: 155回 (15.5%) 4の目: 170回 (17.0%) 5の目: 167回 (16.7%) 6の目: 168回 (16.8%)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    int n, count[6] = {0};
    printf("回数: "); scanf("%d", &n);
    srand((unsigned)time(NULL));

    for (int i = 0; i < 【???】; i++) {
        int die = rand() % 【???】;  // 0〜5
        【???】;
    }

    for (int i = 0; i < 6; i++) {
        printf("%dの目: %d回 (%.1f%%)\n",
            i + 1, count[i], (double)【???】 / n * 100);
    }
    return 0;
}
ループ回数: n回。rand()%6: 0〜5を生成。カウント: count配列の該当インデックスをインクリメント。割合計算: count[i]をdoubleに。
解答:
1つ目: n(n回ループ)
2つ目: 6(0〜5の乱数を生成)
3つ目: count[die]++(出た目のカウントをインクリメント)
4つ目: count[i](各目の出現回数をdoubleに変換)
EX-35 上級
線形合同法による疑似乱数(穴埋め)
線形合同法の漸化式 xn+1 = (a × xn + b) mod m に従って疑似乱数列を生成し、100個の値の平均0.2以上0.4未満の出現割合を求めるプログラムの空欄を埋めよ。
パラメータ: a=717, b=23, m=65536, 初期値 x=1234 とし、y = x / m で 0〜1 の実数に変換する。
0.501 0.211 0.636 0.298 0.626 0.530 0.119 0.238 0.782 0.955 0.923 0.543 0.112 0.315 0.813 0.165 0.005 0.687 0.807 0.307 ...(中略)... 平均は0.505です 0.2以上0.4未満の値がでる割合は0.200です
#include <stdio.h>

int main(void) {
    int m = 65536, a = 717, b = 23, x = 1234;
    int i, count = 0;
    double y, sum = 0.0;

    for (i = 0; 【???】; i++) {
        x = (【???】) % m;
        y = (double)x / m;
        sum += 【???】;

        if (【???】) {
            count++;
        }

        printf("%1.3f ", y);
        if (【???】) {
            printf("\n");
        }
    }
    printf("\n平均は%1.3fです\n", sum / i);
    printf("0.2以上0.4未満の値がでる割合は%1.3fです\n",
           (double)【???】);
    return 0;
}
ループ条件: 100回。漸化式: a*x+b。sumに加算: yの値。範囲判定: y>=0.2 かつ y<0.4。改行条件: 10個ごと。割合: count/i。
解答:
1つ目: i < 100(100回ループ)
2つ目: a * x + b(線形合同法の漸化式)
3つ目: y(合計に y を加算)
4つ目: y >= 0.2 && y < 0.4(0.2以上0.4未満の判定)
5つ目: i % 10 == 9(10個ごとに改行)
6つ目: count / i(出現割合 = count ÷ 全個数)
EX-36 中級
配列の逆順コピー(穴埋め)
配列の要素を逆順に別の配列にコピーする関数の空欄を埋めよ。
#include <stdio.h>

void reverse_copy(int src[], int dst[], int n) {
    for (int i = 0; i < n; i++) {
        dst[i] = src[【???】];
    }
}

int main(void) {
    int a[] = {1, 2, 3, 4, 5};
    int b[5];
    reverse_copy(a, b, 【???】);
    for (int i = 0; i < 5; i++)
        printf("%d ", b[i]);
    printf("\n");  // 出力: 5 4 3 2 1
    return 0;
}
逆順のインデックス: i=0 のとき最後の要素(n-1)、i=1 のとき n-2 ... つまり n - 1 - i
解答:
1つ目: n - 1 - i(逆順のインデックス計算)
2つ目: 5(配列の要素数)
EX-37 上級
行列の積(穴埋め)
2×2行列の積を計算するプログラムの空欄を埋めよ。
A = {{1,2},{3,4}}, B = {{5,6},{7,8}} C[0][0]=19 C[0][1]=22 C[1][0]=43 C[1][1]=50
#include <stdio.h>
#define N 2

int main(void) {
    int A[N][N] = {{1,2},{3,4}};
    int B[N][N] = {{5,6},{7,8}};
    int C[N][N] = {0};

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            for (int k = 0; k < N; k++) {
                C[i][j] += 【???】;
            }
        }
    }

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++)
            printf("C[%d][%d]=%d ", i, j, C[i][j]);
        printf("\n");
    }
    return 0;
}
行列の積: C[i][j] = Σ A[i][k] * B[k][j]。3重ループの内側で足し込みます。
解答:
A[i][k] * B[k][j]
行列積の定義: Cij = Σk Aik × Bkj
EX-38 中級
ポインタと配列(穴埋め)
ポインタを使って配列の各要素を2倍にする関数の空欄を埋めよ。
#include <stdio.h>

void double_array(【???】 arr, int n) {
    for (int i = 0; i < n; i++) {
        【???】 *= 2;
    }
}

int main(void) {
    int data[] = {3, 7, 1, 9};
    double_array(data, 4);
    for (int i = 0; i < 4; i++)
        printf("%d ", data[i]);
    printf("\n");  // 出力: 6 14 2 18
    return 0;
}
関数引数: 配列をポインタで受け取る型は? 要素アクセス: ポインタ経由で i 番目にアクセスするには?
解答:
1つ目: int *(int型ポインタで配列を受け取る)
2つ目: arr[i] または *(arr + i)(ポインタ経由で要素にアクセス)
EX-39 上級
連結リストの挿入(穴埋め)
単方向連結リストの先頭にノードを挿入する関数の空欄を埋めよ。
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node* insert_head(struct Node *head, int val) {
    struct Node *new_node = (struct Node*)malloc(【???】);
    new_node->data = 【???】;
    new_node->next = 【???】;
    return 【???】;
}

int main(void) {
    struct Node *list = NULL;
    list = insert_head(list, 30);
    list = insert_head(list, 20);
    list = insert_head(list, 10);

    struct Node *p = list;
    while (p != NULL) {
        printf("%d -> ", p->data);
        p = p->next;
    }
    printf("NULL\n"); // 出力: 10 -> 20 -> 30 -> NULL
    return 0;
}
malloc: Node1つ分のサイズ。data: 引数の値。next: 元の先頭を指す。return: 新しい先頭ノード。
解答:
1つ目: sizeof(struct Node)(ノード1つ分のメモリを確保)
2つ目: val(引数の値をdataに格納)
3つ目: head(元の先頭ノードを新ノードのnextに設定)
4つ目: new_node(新しい先頭ノードを返す)

問題集と合わせて使えるおすすめ書籍

問題を解いた後、書籍で体系的に復習すると定着率が上がります

📘
苦しんで覚えるC言語
MMGames 著
初心者向けの定番入門書。丁寧な解説で基礎を固められます。
Amazonで見る
📗
新・明解C言語 入門編
柴田望洋 著
図解が豊富で、演習問題も充実。大学の教科書としても採用多数。
Amazonで見る
📙
プログラミング言語C 第2版
B.W.カーニハン, D.M.リッチー 著
通称K&R。C言語の原典。基礎を終えた後のステップアップに最適。
Amazonで見る

※ 上記リンクはアフィリエイトリンクです。購入によりサイト運営を支援いただけます。

この記事をシェア
X(Twitter)でシェア Facebookでシェア LINEで送る はてブ