Menu
Coddy logo textTech

第1メンバーの規則

CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 29/61。

ある構造体を別の構造体の first member として埋め込むと、強力なことが起こります。外側の構造体のメモリアドレスは、その first member のメモリアドレスと同一になります。これは C 標準によって保証されています。

前の例を考えてみましょう:

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // 最初のメンバー
    int grade;
} Child;

Child 変数を作成すると、Parent member は Child の memory のまさに先頭に位置します。つまり、Child へのポインターは、その Parent member へのポインターとまったく同じ address を指します。

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // 安全なキャスト!

2つのポインタは同じ address を保持します。これにより、Child*Parent* を想定する任意の function に渡せます:

void print_id(Parent* p) {
    printf("ID: %d\n", p->id);
}

// main内:
Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // 完璧に動作する

この技法は、Cにおける継承の基盤です。基底型用に記述されたfunctionは、基底型が最初にembeddedされている限り、あらゆる派生型で使用できます。castが安全なのは、memory layoutsがalign perfectlyするbecauseです。

challenge icon

チャレンジ

簡単

単純な動物階層を構築しながら、first member ルールを詳しく見ていきましょう。ベースとなる Animal 構造体と派生した Dog 構造体を作成し、Dog へのポインターを Animal へのポインターへ安全にキャストできる方法を示します。

適用する重要なポイントは次のとおりです。AnimalDog の first member である場合、両方の構造体は同じ開始メモリアドレスを共有します。つまり、ポインターをキャストすることで、Dog*Animal* を要求する任意の function に渡せます。

コードを3つのファイルに分けて整理します。

  • animal.h:include guard を使用して両方の構造体を Define します。Animal 構造体には、単一の field legs(脚の数を表す整数)を持たせます。Dog 構造体には、Animal を first member として embedded し、さらに name field(50文字の文字配列)を追加します。また、Animal* parameter を受け取る function print_animal も Declare します。
  • animal.cprint_animal function を Implement します。この function はベース型を扱います。Animal についてのみ認識し、その animal の脚の数を Print します。
  • main.cDog variable を Create し、その field(embedded された animal の脚と dog の名前の両方)に値を設定します。次に、Dog*Animal* にキャストして print_animal に渡すことで、first member ルールを示します。完全な動作を示すため、dog の名前も別に Print します。

2つの入力を受け取ります。dog の 名前(string)と 脚の数(integer)です。

出力は次のようになります。

Dog: Buddy
Legs: 4

ここで、Buddy は dog の名前、4 は脚の数です。まず main.c から dog の名前を Print し、その後、キャストしたポインターを使って print_animal を call し、脚の数を表示します。

これは、ベース型(Animal)用に書かれた function が、ポインターのキャストによって派生型(Dog)でもシームレスに動作できることを示しています。これは C における継承の基礎です。

自分で試してみよう

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

int main() {
    // 入力を読み取る
    char name[50];
    int legs;
    scanf("%s", name);
    scanf("%d", &legs);
    
    // TODO: Dog変数を作成する
    
    // TODO: strcpyを使って犬の名前を設定する
    
    // TODO: 脚の数を設定する(埋め込まれたAnimal経由でアクセス)
    
    // TODO: 犬の名前を "Dog: <name>" の形式で出力する
    
    // TODO: Dog*をAnimal*にキャストしてprint_animalを呼び出す
    // これは最初のメンバールールを示しています!
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Cオンラインコンパイラ