ポインタ vs 値
CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 9/61。
構造体を関数に渡すとき、Cには2つの選択肢があります。by value(構造体全体をcopyする)で渡すか、by pointer(addressだけをcopyする)で渡すかです。この選択はパフォーマンスに大きく影響します。
値渡しでは、スタック上に構造体の完全なコピーが作成されます。いくつかの整数を持つ小さな構造体なら、これは問題ありません。しかし、数百 bytes の構造体を想像してみてください。every function call で、そのすべてのデータが copies されます。
typedef struct {
char name[100];
int scores[50];
double average;
} Student;
// 値渡し: 呼び出しごとに約308バイトをコピーする
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)を使用します。
pointer 版は、構造体のサイズにかかわらず通常 8 bytes であるメモリの address だけを渡すため、より効率的です。
これが、Cプログラマーがほぼ常に構造体を pointer として渡す理由です。function がデータを変更すべきでない場合は const を使用し、変更すべき場合は通常の pointer を使用します。効率性と意図の明確さの両方を得られます。
チャレンジ
簡単Productモジュールを作成して、構造体を値渡しする場合とポインター渡しする場合の違いを示しましょう。2つの表示関数を作成します。1つは構造体全体をコピーとして受け取り、もう1つは構造体へのポインターだけを受け取ります。
3つのファイルを作成します。
product.h:3つのメンバー、char name[50]、double price、int quantityを持つProduct構造体をDeclareします。また、次の2つの関数をDeclareします。display_by_value:Productを値渡しで受け取り、その情報を表示しますdisplay_by_pointer:const Product *を受け取り、同じ情報を表示します
PRODUCT_Hを使用してインクルードガードを設定します。product.c:両方の関数をImplementします。構文の違いに注目してください。値渡しのバージョンではドット演算子(p.name)を使用し、ポインターバージョンではアロー演算子(p->name)を使用します。両方の関数は同じformatで表示する必要があります。main.c:Productを作成し、両方の表示関数をCallして、異なるパラメーター形式を使用しているにもかかわらず、同一の出力になることを確認します。
3つのinputsを受け取ります。product name(文字列)、price(double)、quantity(整数)です。
mainファイルで、これらの値を使用してProductをinitializeし、その後display_by_value、続けてdisplay_by_pointerをCallします。
両方の関数は次のformatで表示する必要があります。
Product: {name}, Price: {price}, Qty: {quantity}priceには%.2fを使用して、小数点以下2桁まで表示します。
たとえば、inputsがLaptop、999.99、5の場合、出力は次のようになります。
Product: Laptop, Price: 999.99, Qty: 5
Product: Laptop, Price: 999.99, Qty: 5自分で試してみよう
#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変数を作成し、入力値で初期化する
// Hint: strcpyを使ってnameを構造体のnameフィールドにコピーする
// TODO: Productを渡してdisplay_by_valueを呼び出す
// TODO: Productへのポインタを渡してdisplay_by_pointerを呼び出す
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ