Menu
Coddy logo textTech

'Self' ポインタ

CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 7/61。

PythonやJavaのようなオブジェクト指向言語では、object.method()を呼び出す際、言語は自動的にオブジェクト自身をメソッドに渡します。C言語では、構造体へのポインタを第1引数として渡すことで、これを手動で行う必要があります。

このポインタは、慣習的に self (Pythonから借用) または this (C++から) と命名されます。これにより、関数は構造体のデータにアクセスし、読み取りや変更を行うことができます:

typedef struct {
    int health;
} Player;

void player_take_damage(Player *self, int amount) {
    self->health -= amount;
}

selfポインタは、関数をメソッドのように動作させるものです。これによって、どのプレイヤーを修正すべきかを識別できます。これがないと、関数は特定の構造体インスタンスにアクセスする手段がありません。この関数を呼び出すときは、構造体のアドレスを渡します:

Player hero = {100};
player_take_damage(&hero, 25);
// hero.health は現在 75 です

(struct を直接渡すのではなく)ポインタを使用することは、変更を行うために不可欠です。struct を値渡しした場合、function はコピーを受け取ることになり、function が return したときに変更はすべて失われます。ポインタを使用することで、元の struct に対する変更が確実に保持されます。

challenge icon

チャレンジ

簡単

経過時間を秒単位で追跡する Timer モジュールを作成しましょう。構造体の内部状態を、メソッドのように機能する関数を通じて変更する「self」ポインタパターンの練習をします。

3つのファイルを作成します:

  • timer.h: int seconds メンバを1つ持つ Timer 構造体を宣言します。また、2つの関数を宣言します:timer_tick(Timer へのポインタを受け取り、指定された秒数を加算する)と timer_reset(Timer へのポインタを受け取り、秒数をゼロに戻す)です。インクルードガードにはシンボル TIMER_H を使用してください。
  • timer.c: 両方の関数を実装します。timer_tick 関数は Timer *selfint amount を受け取り、タイマーの秒数にその量を追加する必要があります。timer_reset 関数は Timer *self を受け取り、その秒数をゼロに設定する必要があります。
  • main.c: Timer を作成し、操作を実行して、結果を表示します。

2つの整数入力を受け取ります:タイマーの initial seconds(初期秒数)と、tick を介して amount to add(追加する量)です。

main ファイルでは以下の手順を行います:

  1. 初期秒数の値で Timer を初期化します
  2. timer_tick を呼び出して、指定された量を追加します
  3. 現在の秒数を表示します
  4. timer_reset を呼び出してタイマーをリセットします
  5. 再び秒数を表示します(0になるはずです)

結果を以下の形式で出力してください:

After tick: {seconds}
After reset: {seconds}

例えば、初期値が 3015 秒追加する場合、出力は以下のようになります:

After tick: 45
After reset: 0

チートシート

C言語では、構造体を操作する関数は、ポインタパラメータを介して構造体のインスタンスに明示的にアクセスする必要があります。このパラメータは慣習的に self または this と命名されます。

typedef struct {
    int health;
} Player;

void player_take_damage(Player *self, int amount) {
    self->health -= amount;
}

self ポインタにより、関数は構造体のデータにアクセスし、変更することができます。このような関数を呼び出すときは、構造体のアドレスを渡します:

Player hero = {100};
player_take_damage(&hero, 25);
// hero.health is now 75

変更を行うにはポインタの使用が不可欠です。値渡しではコピーが作成され、関数が終了すると変更が失われてしまいます。ポインタを使用することで、元の構造体に変更を永続させることができます。

自分で試してみよう

#include <stdio.h>
#include "timer.h"

int main() {
    int initial_seconds, amount_to_add;
    scanf("%d", &initial_seconds);
    scanf("%d", &amount_to_add);
    
    // TODO: Timerを作成し、initial_secondsで初期化する
    
    // TODO: timer_tickを呼び出してamount_to_add秒を加算する
    
    // TODO: 現在の秒数を次の形式で表示する: "After tick: {seconds}"
    
    // TODO: timer_resetを呼び出してタイマーをリセットする
    
    // TODO: 再度、秒数を次の形式で表示する: "After reset: {seconds}"
    
    return 0;
}
quiz icon腕試し

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

Object Oriented Programmingのすべてのレッスン