第15章の2 構造体の使用例

構造体の使用手順については理解できましたか。

ここでは構造体の使用例として、scanf関数でのデータ入力、構造体の一括代入、構造体の入れ子について説明します。

1. scanf関数を用いてのデータ入力

構造体に scanf関数でデータを入力する場合も通常の変数への入力と変わりありません。
各メンバの型を考えて引数を指定して行ってください。
変数のアドレスをとりだすには、’&’ を変数の前に付け、配列のアドレスをとりだすには、’&’ は不要です。

#include <stdio.h>

struct seiseki {    // 構造体の宣言
    int no;
    char name[20];
    double average;
};

int main(void)
{
    struct seiseki seito1, seito2[20];    // 構造体の変数と配列の宣言

    // 構造体の変数に scanfでデータを入力
    scanf("%d", &seito1.no);
    scanf("%s", seito1.name);
    scanf("%lf", &seito1.average);
    
    // 構造体の配列に scanfでデータを入力
    for(int i = 0; i < 3; i++) {
        scanf("%d", &seito2[i].no);
        scanf("%s", seito2[i].name);
        scanf("%lf", &seito2[i].average);
    }
    // 入力データの確認
    printf("no:%d name:%s average:%f\n", seito1.no, seito1.name, seito1.average);
    for(int i = 0; i < 3; i++) {
        printf("no:%d name:%s average:%f\n", 
            seito2[i].no, seito2[i].name, seito2[i].average);
    }
    
    return 0;
}

【実行結果例】
123 田中 78.4
124 千葉 81.3
125 土田 73.3
126 手塚 68.2

no:123 name:田中 average:78.400000
no:124 name:千葉 average:81.300000
no:125 name:土田 average:73.300000
no:126 name:手塚 average:68.200000

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

2. 構造体の一括代入

同一の型を持った構造体は一括して代入が可能です。

#include <stdio.h>

struct seiseki {
    int no;
    char name[20];
    double average;
};

int main(void)
{
    struct seiseki seito1 = { 5, "KASAHARA", 83.5 };
    struct seiseki seito2, seito3[20];
    
    // 構造体変数の一括代入
    seito2 = seito1;        
    seito3[1] = seito1;

     /* seito2 と seito3[1] の各メンバには、
        seito1 の内容がそのまま代入された */
    printf("seito1    %d %s %.1f\n", seito1.no, seito1.name, seito1.average);
    printf("seito2    %d %s %.1f\n", seito2.no, seito2.name, seito2.average);
    printf("seito3[1] %d %s %.1f\n", seito3[1].no, seito3[1].name, seito3[1].average);

    return 0;
}

【実行結果例】
seito1 5 KASAHARA 83.5
seito2 5 KASAHARA 83.5
seito3[1] 5 KASAHARA 83.5

3. 構造体の入れ子

構造体の中に構造体を入れ子にして宣言することが可能です。

#include <stdio.h>

struct kyuuyo {    // 給与
    int kihon;       // 基本給
    int jyutaku;     // 住宅手当
    int kazoku;      // 家族手当
    int sikaku;      // 資格手当
} kyu;

struct syain_dt {   // 社員情報
    int no;            // 社員番号
    char name[20];     // 氏名
    char yaku[20];     // 役職
    int nensu;         // 勤続年数
    struct kyuuyo kyu; // 給与
};

構造体の入れ子:
先に宣言した構造体を入れ子にして用いる。
入れ子に用いる構造体は必ず先に宣言すること。

構造体の全体図
int main(void)
{
    struct syain_dt syomu[20]= {    // 庶務課
        {78027, "神保直樹", "課長", 21, {446780, 50000, 30000, 20000}},
        {84004, "相原彰子", "主任", 15, {363640, 50000, 20000, 20000}},
        {87022, "本郷幸子", "", 12, {308760, 50000, 10000, 20000}},
        {93042, "三上葵", "", 6, {286530, 30000, 0, 10000}},
        {95005, "佐々木翠", "", 4, {276700, 0, 0, 20000}},
        {99009, "長崎宏美", "", 1, {250140, 0, 0, 0}},
        {0, "", "", 0, {0, 0, 0, 0} },
    };
    for (int i = 0; syomu[i].no != 0; i++) {
        printf("%-10s 総支給額 = %d\n", 
            syomu[i].name, 
            syomu[i].kyu.kihon + syomu[i].kyu.jyutaku + 
            syomu[i].kyu.kazoku + syomu[i].kyu.sikaku);
    }

    return 0;
}


初期化は全てのメンバを順に並べて行う。

メンバの参照はドット演算子(.)を2つ用いて行う。

【実行結果例】
神保直樹 総支給額 = 546780
相原彰子 総支給額 = 453640
本郷幸子 総支給額 = 388760
三上葵  総支給額 = 326530
佐々木翠 総支給額 = 296700
長崎宏美 総支給額 = 250140

入れ子の構造体は、次のようにまとめて記述することも可能です。

struct syain_dt {  // 社員情報
    int no;           // 社員番号
    char name[20];    // 氏名
    char yaku[20];    // 役職
    int  nensu;       // 勤続年数
    struct kyuuyo {   // 給与
        int kihon;       // 基本給
        int jyutaku;     // 住宅手当
        int kazoku;      // 家族手当
        int sikaku;      // 資格手当
    } kyu;
};

〇 演習問題

図書データを設定する下記のような構造体配列がある。同じ構造の作業用の構造体変数に、図書名、返却予定年月日を、返却予定年に0が入力されるまで入力しなさい。

入力した図書が構造体配列にすでに登録されている場合は「貸し出し中」をメッセージ出力し、未登録の場合は構造体配列に登録しなさい。

【実行結果例】
図書名、年月日を入力しなさい。(終了条件:年が0)
星を継ぐもの 2025 6 3
貸し出しOKです。
図書名、年月日を入力しなさい。(終了条件:年が0)
巨人たちの星 2025 6 10
貸し出しOKです。
図書名、年月日を入力しなさい。(終了条件:年が0)
星を継ぐもの 2025 6 15
貸し出し中です。別の本を指定してください。
三体 2025 6 15
貸し出しOKです。
図書名、年月日を入力しなさい。(終了条件:年が0)
0 0 0 0
処理を終了します。
「星を継ぐもの」 2025.06.03
「巨人たちの星」 2025.06.10
「三体」 2025.06.15

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

解答例

#include <stdio.h>
#include <string.h>

#define ULIMIT 100    // 配列数上限

struct date {    // 日付
    int yy;        // 年
    int mm;        // 月
    int dd;        // 日
};
struct tosyo {   // 図書データ
    char name[100];        // 図書名
    struct date henkyaku;  // 返却予定日
};

int main(void)
{
    struct tosyo work;
    struct tosyo rireki[ULIMIT];
    int i, cnt = 0;

    printf("図書名、年月日を入力しなさい。(終了条件:年が0)\n");
    while (1) {
        scanf("%99s %d %d %d", work.name, &work.henkyaku.yy,
            &work.henkyaku.mm, &work.henkyaku.dd);
        // 返却予定年が0かなら登録終了
        if (work.henkyaku.yy == 0) {
            printf("処理を終了します。\n");
            break;
        }
        for (i = 0; i < cnt; i++) {
            // 貸し出し中
            if (strcmp(work.name, rireki[i].name) == 0) break;
        }
        if (i < cnt) {
            printf("貸し出し中です。別の本を指定してください。\n");
            continue;
        }

        // 貸し出しOK
        printf("貸し出しOKです。\n");
        rireki[cnt++] = work;

        // 配列上限なら終了
        if (cnt >= ULIMIT) {
            printf("登録冊数に達しました。処理を終了します。\n");
            break;
        }

        printf("図書名、年月日を入力しなさい。(終了条件:年が0)\n");
    }

    for (i = 0; i < cnt; i++) {
        printf("「%s」 %4d.%02d.%02d\n", rireki[i].name,
            rireki[i].henkyaku.yy, rireki[i].henkyaku.mm, rireki[i].henkyaku.dd);
    }

    return 0;
}

コメント