ポインタ vs 値
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 9/61。
構造体を関数に渡す際、C言語では2つの選択肢があります。値渡し(構造体全体をコピーする)か、あるいはポインタ渡し(アドレスのみをコピーする)です。この選択はパフォーマンスに大きく影響します。
値渡しを行うと、スタック上に構造体の完全なコピーが作成されます。数個の整数を持つ小さな構造体であれば、これで問題ありません。しかし、数百バイトもある構造体を想像してみてください。関数を呼び出すたびに、それらすべてのデータがコピーされることになります。
typedef struct {
char name[100];
int scores[50];
double average;
} Student;
// 値渡し: 呼び出しごとに約608バイトをコピーします
void print_by_value(Student s) {
printf("%s: %.2f\n", s.name, s.average);
}
// ポインタ渡し: 8バイト(アドレス)のみをコピーします
void print_by_pointer(const Student *s) {
printf("%s: %.2f\n", s->name, s->average);
}
構文も異なります。値パラメータの場合、ドット演算子(s.name)を使用します。ポインタの場合、アロー演算子(s->name)を使用します。
ポインタ版は、構造体のサイズに関係なく、通常は8バイトのメモリアドレスのみを渡すため、より効率的です。
これが、Cプログラマがほとんどの場合、構造体をポインタで渡す理由です。関数がデータを変更すべきでない場合は const を使用し、変更すべき場合には通常のポインタを使用します。これにより、効率性と明確な意図の両方を得ることができます。
チャレンジ
簡単構造体を値渡しする場合とポインタ渡しする場合の違いを実証する Product モジュールを作成しましょう。2つの表示関数を作成します。1つは構造体全体をコピーとして受け取り、もう1つはその構造体へのポインタのみを受け取ります。
3つのファイルを作成します:
product.h: 3つのメンバ(char name[50]、double price、int quantity)を持つProduct構造体を宣言します。また、2つの関数を宣言します:display_by_value—Productを値渡しで受け取り、その情報を表示しますdisplay_by_pointer—const Product *を受け取り、同じ情報を表示します
PRODUCT_Hを使用してインクルードガードを設定してください。product.c: 両方の関数を実装します。構文の違いに注意してください。値渡し版ではドット演算子(p.name)を使用し、ポインタ版ではアロー演算子(p->name)を使用します。両方の関数は同じ形式で出力する必要があります。main.c: Product を作成し、両方の表示関数を呼び出して、パラメータの形式が異なっても同じ出力が生成されることを確認します。
3つの入力を受け取ります:製品名(文字列)、価格(double)、数量(整数)。
メインファイルで、これらの値を使用して Product を初期化し、display_by_value を呼び出した後に display_by_pointer を呼び出してください。
両方の関数は次の形式で出力する必要があります:
Product: {name}, Price: {price}, Qty: {quantity}価格には %.2f を使用して、小数点以下2桁まで表示してください。
例えば、入力が Laptop、999.99、5 の場合、出力は次のようになります:
Product: Laptop, Price: 999.99, Qty: 5
Product: Laptop, Price: 999.99, Qty: 5チートシート
構造体を関数に渡す際、**値渡し**(構造体全体をコピーする)か**ポインタ渡し**(アドレスのみをコピーする)のいずれかを選択できます。
**値渡し**は、構造体の完全なコピーを作成します:
void print_by_value(Student s) {
printf("%s: %.2f\n", s.name, s.average); // ドット演算子を使用する
}
**ポインタ渡し**は、メモリアドレスのみを渡します:
void print_by_pointer(const Student *s) {
printf("%s: %.2f\n", s->name, s->average); // アロー演算子を使用する
}
主な違い:
- 値渡しは**ドット演算子**(
s.member)を使用します - ポインタ渡しは**アロー演算子**(
s->member)を使用します - ポインタは大きな構造体に対してより効率的です(構造体全体のサイズに対して、わずか8バイトです)
- 関数がデータを変更すべきでない場合は、ポインタに
constを使用します
Cプログラマーは通常、パフォーマンス向上のために構造体をポインタで渡すことを好みます。
自分で試してみよう
#include <stdio.h>
#include <string.h>
#include "product.h"
int main() {
char name[50];
double price;
int quantity;
// 入力を読み込む
scanf("%s", name);
scanf("%lf", &price);
scanf("%d", &quantity);
// TODO: Product 変数を作成し、入力値で初期化する
// ヒント: strcpy を使用して、構造体の name フィールドに名前をコピーする
// TODO: Product を引数に指定して display_by_value を呼び出す
// TODO: Product へのポインタを引数に指定して display_by_pointer を呼び出す
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。