構造体ポインタの受け渡し
CoddyのCジャーニー「Logic & Flow」セクションの一部 — レッスン 47/63。
構造体を値渡しすることはデータの読み取りには適していますが、構造体全体がコピーされるため、大きな構造体では非効率になります。さらに重要なことに、関数内から元の構造体を変更することはできません。ここで、構造体へのポインタを渡すことが不可欠になります。
構造体そのものではなく、構造体へのポインタを渡すと、関数は元の構造体のメモリアドレスを受け取ります。この手法には2つの大きな利点があります。1つは(コピーが発生しないため)より効率的であること、もう1つは関数が元のデータを変更できることです。
構造体へのポインタを受け取る関数の構文は次のとおりです。
void functionName(struct StructureName *ptr) {
// ptr->member を使用する関数本体
}関数内では、アロー演算子を使用して構造体のメンバにアクセスし、変更します。ポインタを介して行われた変更は、呼び出し元の関数内にある元の構造体に直接影響を与えるため、この手法は構造体データを更新または変換する必要がある関数に最適です。
この手法は、ポインタの強力さと構造体の組織化を組み合わせることで、複雑なデータ構造を扱う効率的なCプログラムを構築するための基本となります。
チャレンジ
簡単効率的なデータ変更のために、構造体へのポインタを関数に渡す方法を示すC言語プログラムを作成してください。プログラムは以下の要件を満たす必要があります:
- 以下のメンバを持つ
BankAccountという名前のstructを定義します:- 口座番号を格納する整数型の
accountNumber - 口座名義人の名前を格納するサイズ30の文字配列
ownerName - 口座残高を格納する浮動小数点型の
balance - 取引回数を格納する整数型の
transactionCount
- 口座番号を格納する整数型の
- 以下の処理を行う
depositMoneyという名前の関数を記述します:BankAccount構造体へのポインタと、浮動小数点型の金額をパラメータとして受け取ります- アロー演算子を使用して、口座残高に金額を加算します
- 取引回数を1増やします
"Deposit successful. New balance: %.2f\n"という形式で出力します([balance]の部分に値を入れます)
- 以下の処理を行う
withdrawMoneyという名前の関数を記述します:BankAccount構造体へのポインタと、浮動小数点型の金額をパラメータとして受け取ります- 引き出し額が現在の残高より大きいかどうかを確認します
- 残高不足の場合、
"Insufficient funds. Current balance: %.2f\n"と出力します - 残高が十分な場合、残高から金額を減算し、取引回数を増やし、
"Withdrawal successful. New balance: %.2f\n"と出力します
- 以下の処理を行う
displayAccountという名前の関数を記述します:BankAccount構造体へのポインタをパラメータとして受け取ります- 以下の正確な形式で口座情報を出力します:
"Account Information:\n""Account Number: %d\n""Owner: %s\n""Balance: %.2f\n""Transactions: %d\n"
- main関数内で以下の処理を行います:
accountという名前のBankAccount変数を作成します- 入力から口座番号、名義人名、初期残高を読み取ります
- 取引回数を0に設定します
displayAccountを呼び出して、初期の口座状態を表示します- 入金額を読み取り、
depositMoneyを呼び出します - 引き出し額を読み取り、
withdrawMoneyを呼び出します displayAccountを呼び出して、最終的な口座状態を表示します
このチャレンジでは、構造体へのポインタを関数に渡すことの利点を示します。値渡しとは異なり、関数は元の構造体のメモリ番地を受け取るため、元のデータを直接変更することができます。このアプローチは(コピーが発生しないため)より効率的であり、呼び出し元の関数にある構造体の値を関数が永続的に変更することを可能にします。
チートシート
構造体を値渡しする場合、構造体全体がコピーされます。これは大きな構造体にとっては非効率であり、元のデータを変更することもできません。**構造体へのポインタ**を渡すことで、代わりにメモリ番地を渡し、これら両方の問題を解決できます。
構造体へのポインタを受け取るための関数の構文:
void functionName(struct StructureName *ptr) {
// ptr->member を使用した関数本体
}関数内では、**アロー演算子** (->) を使用して、ポインタを介して構造体のメンバにアクセスし、変更します。加えられた変更は、呼び出し元の関数にある元の構造体に直接影響します。
このアプローチには、主に2つの利点があります:
- より効率的(構造体全体のコピーが発生しない)
- 関数が元の構造体データを変更できる
自分で試してみよう
#include <stdio.h>
#include <string.h>
// TODO: ここで BankAccount 構造体を定義します
// TODO: ここで depositMoney 関数を記述します
// TODO: ここで withdrawMoney 関数を記述します
// TODO: ここで displayAccount 関数を記述します
int main() {
// 入力を読み込む
int accountNum;
char ownerName[30];
float initialBalance;
float depositAmount;
float withdrawAmount;
scanf("%d", &accountNum);
scanf("%s", ownerName);
scanf("%f", &initialBalance);
scanf("%f", &depositAmount);
scanf("%f", &withdrawAmount);
// TODO: BankAccount 変数を作成し、初期化します
// TODO: displayAccount を呼び出して初期状態を表示します
// TODO: 入金額を指定して depositMoney を呼び出します
// TODO: 出金額を指定して withdrawMoney を呼び出します
// TODO: displayAccount を呼び出して最終状態を表示します
return 0;
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。