Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年3月3日に更新

第2章45 構造体を使う

イチからゲーム作りで覚えるC言語
第2章44 malloc関数、free関数で大きなメモリを扱う : PREV
NEXT : 第2章46 データ構造・スタックの実装 :

概要

C言語で構造体を扱う方法を確認していきます。 構造体を使うことで、自分が作りたいオリジナルの型のようなものを作成し、 いくつかの変数をまとめて一つの変数で管理すること等が可能になります。

構造体の定義

早速ですが、キャラクタの名前、レベル、体力を1つの構造体で管理してみましょう。 下の用に定義することができます。

 
CharacterStatus構造体
struct CharacterStatus {
    char const *name;
    int level;
    int hitpoint;
};

structという新しいキーワードが出てきています。 struct は日本語に直訳すると「構造」という意味です。 下のように書くことができます。

構造体の構文
struct 構造体の名前 { 構造体の中身の構成 };

構造対の中身の構成は、例では 3 つ(名前、レベル、体力)でしたが、他にも自由に追加できます。

オブジェクト指向プログラミング

C言語の構造体を使うことで、オブジェクト指向プログラミングの考えを部分的に 取り入れやすくなります。 例えば Java 言語の クラス に近い感覚で情報を扱うことも可能です。 ただし、オブジェクト指向プログラミングをそもそも言語機能として取り込んではいないため、 コードは若干、読みづらいものになりがちです。

構造体を使ったコード例

構造体の宣言、定義、初期化、データの読み出しの流れを確認していきます。

 
strct_ex1.c
#include <stdio.h>
struct GameCharacter;
struct GameCharacter {
    char const *name;
    int level;
    int hitpoint;
};

void printCharacterStatus(struct GameCharacter *c) {
    printf("============\n");
    printf("名前:%s\n", c->name);
    printf("レベル:%d\n", c->level);
    printf("HP:%d\n", c->hitpoint);
}

int main(){
    struct GameCharacter planc;
    planc.name = "planc";
    planc.level = 1;
    planc.hitpoint = 5;

    struct GameCharacter enemy;
    enemy.name = "Charotte";
    enemy.level = 2;
    enemy.hitpoint = 8;

    printCharacterStatus( &planc );
    printCharacterStatus( &enemy );
}

先ほどの例と同じ構造体、GameCharacter を使っていきます。

実行すると下の結果になりました。

結果
============
名前:planc
レベル:1
HP:5
============
名前:Charotte
レベル:2
HP:8

構造体のプロトタイプ宣言と定義

今回は構造体 GameCharacter はあちこちで使いまわすことを考え、 main 関数のなかではなく、ソースコードの冒頭のグローバル領域の場所でプロトタイプ宣言しています。

 
strct_ex1.c
struct GameCharacter;

関数のプロトタイプ宣言と同じで、いろいろなソースコードから参照して使えるようにするため、 通常、ヘッダーファイルの中で宣言を書いておき、 実際の定義(中身の構造など)は拡張子 .C の中に書いておきます

今回も説明のためにプロトタイプ宣言していますが、この行は無くてもコンパイラはちゃんと動きます。

続いて、3~7行目で構造体の定義をしています。

 
strct_ex1.c
struct GameCharacter {
    char const *name;
    int level;
    int hitpoint;
};

具体的に、どのような値を持っているかを定義していきます。

続いて、 main 関数を見ていきましょう。

 
strct_ex1.c
int main(){
    struct GameCharacter planc;
    planc.name = "planc";
    planc.level = 1;
    planc.hitpoint = 5;

    struct GameCharacter enemy;
    enemy.name = "Charotte";
    enemy.level = 1;
    enemy.hitpoint = 5;

16 行目で、構造体を使った変数 planc を定義しています。 構造体を使うには、以下のように記述します。

構造体の利用
struct 構造体の名前  変数の名前;

int 型や char型などと同じように、複数の変数を一度に宣言することもできます。

構造体の変数宣言を複数に
struct GameCharacter planc, enemy;

構造体の中身へのアクセス

続いて、17 行目からは構造体の変数の中身を初期化しています。

 
strct_ex1.c
    planc.name = "planc";
    planc.level = 1;
    planc.hitpoint = 5;

変数 planc の構造体の中身、 name 、level、hitpoint にアクセスして、それぞれ初期化しています。

構造体の変数の中身にアクセスするときは下のように書きます。

構造体の変数名.アクセスしたい要素名

ですので、変数 planc の構造体の level に値 1 を代入したい場合、 「planc.level = 1」と書けば OK です。

変数 planc、enemy の定義と初期化が終わったのち、 自前の関数 printCharacterStatusを呼び出しています。

 
strct_ex1.c
    printCharacterStatus( &planc );
    printCharacterStatus( &enemy );

printCharacterStatus は構造体 GameCharacter のアドレスを受け取り、 中身(キャラクターのステータス)を表示する関数です。

では、printCharacterStatus 関数の中身を見てみましょう。

 
strct_ex1.c
void printCharacterStatus(struct GameCharacter *c) {
    printf("============\n");
    printf("名前:%s\n", c->name);
    printf("レベル:%d\n", c->level);
    printf("HP:%d\n", c->hitpoint);
}

printCharacterStatus 関数は構造体 GameCharacter のポインタを引数で受け取るように 定義されています。

ポインタ変数経由での構造体の中身へのアクセス

受け取ったポインタ変数 c へのアドレスを基準に、名前やレベル、ヒットポイントなどの 構造体の中身のアドレスへアクセスが出来ます。

具体的には、下のように書くことで、構造体の中身のアドレスへアクセスできます。

構造体のポインタ変数->アクセスしたい要素名

一般に「->」の記号は右矢印みたいに見えるので、 アロー演算子(Arrow Operator) と呼ばれることが多いです。

例えば、構造体 GameCharacter のポインタ変数 c の構造体の中身、name には 「c->name」でアクセスすることができます。

 
strct_ex1.c
    printf("名前:%s\n", c->name);

構造体に別名をつける

構造体を使うとき、いちいち、struct 構造体名 変数名; と書くのはちょっとめんどくさいので、 例えば、typedef ディレクティブを使って別名を定義することで、もうすこし簡潔に書くことができます。

struct_typedef.c
#include <stdio.h>
struct GameCharacter {
    char const *name;
    int level;
    int hitpoint;
};
typedef struct GameCharacter gc;
int main(){
    gc planc;
    planc.name = "ぷらんく";
    planc.level = 5;
    planc.hitpoint = 100;
}

このようにtypedef struct GameCharacter gc;と書いておくことで、 以降、「struct GameCharacter」の代わりに 「gc」と記述することができます。

また、構造体の定義と typedef を一つにまとめて、下の用に書くことができます。

struct_typedef2.c
typedef struct GameCharacter {
    char const *name;
    int level;
    int hitpoint;
} gc;

このとき、構造体本体の名前は省略することができます。

struct_typedef2.c
typedef struct {
    char const *name;
    int level;
    int hitpoint;
} gc;

typedef で「struct{~}」を「gc」で記載できるように型を定義、ということをしています。

構造体の変数の定義と初期化を同時に行う

構造体の変数の定義と初期化は同時に行うことができます。

 
strct_ex3.c
#include <stdio.h>
typedef struct GameCharacter {
    char const *name;
    int level;
    int hitpoint;
} gc;

void printCharacterStatus(gc *c) {
    printf("============\n");
    printf("名前:%s\n", c->name);
    printf("レベル:%d\n", c->level);
    printf("HP:%d\n", c->hitpoint);
}

int main(){
    gc planc = {"planc", 1, 5};
    gc enemy = {"Charotte", 2, 8};
    printCharacterStatus( &planc );
    printCharacterStatus( &enemy );
}

16 行目、17行目で、変数 planc、enemy の宣言と初期化を同時に行っています。 gc(つまり struct GameCharacter のことです)の中身を、"{"、" }"の中の要素で 順番に初期化できます。

宣言と初期化
gc planc = {"planc", 1, 5};

このコードは下と同じ結果になります。

宣言と初期化
gc planc;              // 変数の宣言
planc.name = "planc";  // 変数の初期化
planc.level = 1;       // 変数の初期化
planc.hitpoint = 5;    // 変数の初期化

これでだいぶコードがスッキリしました!

参考

イチからゲーム作りで覚えるC言語
第2章44 malloc関数、free関数で大きなメモリを扱う : PREV
NEXT : 第2章46 データ構造・スタックの実装 :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

第4章3 C言語コンソール上で迷路脱出プログラム・迷路の自動生成

#C11仕様#C言語#ゲームプログラミング✎ 2021-05-15
C言語のコンソールゲームで迷路脱出ゲームプログラムの作り方を確認します。迷路は自動生成されます
広告領域
追従 広告領域
目次
第2章45 構造体を使う
第2章45 構造体を使う
概要
概要
構造体の定義
構造体の定義
構造体を使ったコード例
構造体を使ったコード例
構造体のプロトタイプ宣言と定義
構造体のプロトタイプ宣言と定義
構造体の中身へのアクセス
構造体の中身へのアクセス
ポインタ変数経由での構造体の中身へのアクセス
ポインタ変数経由での構造体の中身へのアクセス
構造体に別名をつける
構造体に別名をつける
構造体の変数の定義と初期化を同時に行う
構造体の変数の定義と初期化を同時に行う
参考
参考
Nodachisoft © 2021