スタック初期化
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 14/61。
すべてのオブジェクトがヒープ上に存在する必要はありません。構造体が小さく、寿命が短い場合、スタック上に割り当てる方が簡単で高速です。malloc も free も不要で、メモリリークのリスクもありません。
ポインタを返す代わりに、ファクトリ関数は構造体を値渡しで返すことができます。構造体全体が呼び出し元にコピーされます:
typedef struct {
int x;
int y;
} Point;
Point create_point(int x, int y) {
Point p;
p.x = x;
p.y = y;
return p;
}
呼び出し元は、完全に初期化されたコピーを受け取ります:
Point origin = create_point(0, 0);
Point cursor = create_point(100, 50);
printf("Cursor: (%d, %d)\n", cursor.x, cursor.y);
ポインタはなく、クリーンアップも不要です。origin と cursor がスコープ外になると、それらは自動的に破棄されます。
このアプローチは、動的に割り当てられたメンバを持たない小さな構造体に最適です。より大きなオブジェクトや、ヒープメモリへのポインタを含むものに対しては、malloc を使用したコンストラクタパターンが引き続きより良い選択肢となります。
チャレンジ
簡単ファクトリ関数を使用してスタック上に温度の測定値を作成する Temperature モジュールを構築しましょう。以前のレッスンのヒープベースのコンストラクタとは異なり、このアプローチでは構造体を値で返します。malloc も free も不要で、シンプルかつ効率的なスタック割り当てのみを使用します。
3つのファイルを作成します:
temperature.h: 2つのメンバを持つTemperature構造体を宣言します:double celsiusとchar scale(摂氏を表す'C'を格納するため)。また、2つの関数を宣言します:create_temperature— double 値を受け取り、Temperature構造体を値で返します(ポインタではありません!)。temperature_to_fahrenheit—const Temperature *を受け取り、対応する華氏の温度を double として返します。
TEMPERATURE_Hを使用してください。temperature.c: 両方の関数を実装します。ファクトリ関数は、ローカルのTemperature変数を作成し、そのフィールドを初期化し(scaleを'C'に設定)、それを返す必要があります。変換式はfahrenheit = celsius * 9.0 / 5.0 + 32.0です。main.c: ファクトリ関数を使用して温度の測定値を作成し、摂氏と華氏の両方の値を表示します。
1つの入力を受け取ります:摂氏の温度(double 型)。
メインファイルで、create_temperature を使用して、与えられた値を持つ Temperature 構造体を作成します。次に、元の摂氏の値とその華氏の換算値を表示します。
出力は次の形式で表示してください:
Celsius: {celsius}
Fahrenheit: {fahrenheit}両方の値に %.2f を使用して、小数点以下2桁まで表示してください。
例えば、入力が 25.0 の場合、出力は次のようになります:
Celsius: 25.00
Fahrenheit: 77.00このアプローチがいかにクリーンであるかに注目してください。メモリ管理の心配をすることなく、完全に初期化された構造体を受け取ることができます。Temperature 変数がスコープ外になると、自動的にクリーンアップされます。
チートシート
ファクトリ関数は、ポインタではなく値渡しで構造体を返すことができます。これにより、構造体はスタック上に割り当てられます。malloc も free も不要で、メモリリークも発生しません。
値渡しで返すファクトリ関数の例:
typedef struct {
int x;
int y;
} Point;
Point create_point(int x, int y) {
Point p;
p.x = x;
p.y = y;
return p;
}
ファクトリ関数の使用例:
Point origin = create_point(0, 0);
Point cursor = create_point(100, 50);
printf("Cursor: (%d, %d)\n", cursor.x, cursor.y);
構造体は呼び出し元にコピーされ、スコープを抜けると自動的に破棄されます。この手法は、動的に割り当てられたメンバを持たない小さな構造体に最適です。
自分で試してみよう
#include <stdio.h>
#include "temperature.h"
int main() {
double celsius_input;
scanf("%lf", &celsius_input);
// TODO: create_temperature を使用して Temperature 構造体を作成する
// TODO: %.2f フォーマットを使用して摂氏(Celsius)の値を出力する
// TODO: temperature_to_fahrenheit を使用して華氏(Fahrenheit)の値に変換し、出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。