C言語における構造体の利用方法

目次

構造体

構造体とは何か?

なぜC言語に構造体が導入されたのか?

構造体型の宣言

構文構造

練習問題

構造体メンバの型

構造体変数の定義

構造体の初期化

構造体メンバへのアクセス

構造体の関数渡し

構造体

今年の立秋は早かったですね。皆さんは初めての秋のカフェオレを飲めましたか?今日は構造体について詳しく説明します。この記事では基本的な内容を扱い、今後の詳細な解説につなげていきます。

構造体とは?

構造体は複数の値をまとめるデータ型であり、それぞれの値は「メンバ」と呼ばれます。各メンバは異なる型を持つことができます

C言語に構造体が導入された理由

C言語にはintやchar、doubleなどの基本型がありますが、これらは単一の情報を表現するだけです。複雑なオブジェクトを表現するには不十分です。たとえば、「人」を表現する場合、性別、年齢、身長、体重など複数の情報を同時に扱う必要があります。このような場合に構造体が役立ちます。

構造体型の宣言

構文構造

struct タグ名
{
    メンバリスト;
} 変数リスト;

struct キーワードは省略できません

タグ名 独自に定義した構造体名(実際の用途に応じて命名)

メンバリスト メンバ変数の一覧

変数リスト 変数の一覧

練習問題

ここでは学生情報の構造体を作成してみましょう。

//学生型------→学生変数--------→学生の属性情報

#include <stdio.h>
struct Student
{
    char name[10];
    char gender[5];
    int age;
};

int main()
{
    return 0;
}

構造体メンバの型

構造体のメンバには様々な型が使用できます。スカラー値、配列、ポインタ、他の構造体なども可能です。

struct Student
{
    int id;            // スカラー値
    char name[10];     // 配列
    char gender[5];
    int* ptr;          // ポインタ
    struct Class;      // 他の構造体
    struct Class* cptr; // 構造体ポインタ
};

int main()
{
    return 0;
}

構造体変数の定義

グローバル変数、ローカル変数、別名による定義方法があります。

// グローバル・ローカル変数
#include <stdio.h>

struct Student
{
    char name[10];
    char gender[5];
    int age;
}s3, s4;              // s3, s4 はグローバル変数

struct Student s5;   // s5 もグローバル変数

int main()
{
    struct Student s1;
    struct Student s2;
    // s1, s2 はローカル変数
    return 0;
}
// 別名による定義
#include <stdio.h>

typedef struct Student
{
    char name[10];
    char gender[5];
    int age;
} STUDENT;

int main()
{
    struct Student s1;
    struct Student s2;
    // 別名を使った定義も可能
    STUDENT s6;  // ローカル変数
    return 0;
}

構造体の初期化

初期化方法には「順序による初期化」と「特定メンバによる初期化」の2種類があります。

上記「構造体変数の定義」で紹介したすべての方法に適用可能です。

#include <stdio.h>

struct InnerStruct
{
    int key;
};

struct OuterStruct
{
    char ch;
    int value;
    int array[10];
    double* ptr;
    struct InnerStruct inner;
    struct InnerStruct* iptr;
};

int main()
{
    double val = 3.14;

    // 順序による初期化
    struct OuterStruct s3 = { 'q', 100, {1,2,3,4}, &val, {99}, NULL };

    // 特定メンバによる初期化
    struct OuterStruct s4 = { .value = 1000, .array = {1,2,3,4,5} };

    // 部分的に初期化され、残りはゼロクリア
    return 0;
}

構造体メンバへのアクセス

  • . 構造体.メンバ
  • -> 構造体->メンバ
#include <stdio.h>

struct Book
{
    char title[20];
    int cost;
};

void print_info(struct Book b)
{
    printf("%s %d\n", b.title, b.cost);
}

void print_ptr(struct Book* pb)
{
    printf("%s %d\n", (*pb).title, (*pb).cost);
    printf("%s %d\n", pb->title, pb->cost);
}

int main()
{
    struct Book book = { "C言語入門", 55 };
    print_info(book);         // 値渡し
    print_ptr(&book);         // アドレス渡し
    printf("%s %d\n", book.title, book.cost);

    struct Book* ptr = &book;
    printf("%s %d\n", ptr->title, ptr->cost);  // ポインタと組み合わせる
    return 0;
}

構造体の関数渡し

練習:構造体sにデータを設定する関数を作成してください。

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

struct Data
{
    char name[20];
    int age;
};

void set_data(struct Data t)  // 値渡し
{
    t.age = 18;
    strcpy(t.name, "zhangsan");
    // t.name = "zhangsan"; ❌ 配列名は定数のアドレスなので代入不可
}

int main()
{
    struct Data s = { "name", 0 };
    set_data(s);
    printf("%s %d\n", s.name, s.age);
    return 0;
}

なぜsにデータが保存されなかったのでしょうか?これは以前の配列と関数の交換処理で学んだ内容と同じです。

実引数から仮引数へ渡されるとき、仮引数は実引数のコピーであり、仮引数の変更は実引数に影響しません。

よって、関数内で実引数を変更したい場合は、そのアドレスを渡す必要があります。

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

struct Data
{
    char name[20];
    int age;
};

void set_data(struct Data* t)  // アドレス渡し
{
    t->age = 19;
    strcpy(t->name, "zhangsan");
}

int main()
{
    struct Data s = { "name", 0 };
    set_data(&s);  // 実引数としてアドレスを渡す
    printf("%s %d\n", s.name, s.age);
    return 0;
}

関数呼び出し時に変数を渡すか、アドレスを渡すかはどのように選択すべきでしょうか?

アドレス渡しを選ぶべきです

関数に引数を渡す際、スタックに積まれる必要があります。

構造体が大きい場合、スタックへの積み込みが大きくなり、パフォーマンスが低下します。

構造体を関数に渡す際は、アドレスを渡すのが望ましいです。

✔✔✔最後に、ご閲覧ありがとうございました。間違いや改善点があれば、ご指摘いただけますと幸いです!🙂

コード---------------------→【gitee:https://gitee.com/TSQXG】

連絡---------------------→【メール:2784139418@qq.com

タグ: C言語 構造体 データ型 関数パラメータ プログラミング

6月17日 23:13 投稿