CやC++でメモリを確保したら必ず解放する。ファイルを開いたら必ず閉じる。この「後始末」を忘れるとバグが生まれます。しかし、エラーハンドリングが複雑になるほど後始末を漏らしやすくなります。そこで注目されているのがdefer構文です。今回は、GCCとClangのdefer対応の現状と実装パターンを整理します。

GCCとClangのdefer対応の背景

Go言語にはdeferというキーワードがあります。つまり、関数の終了時に実行する処理を事前に登録できる仕組みです。これがリソース管理を劇的に簡単にしました。しかし、CやC++にはこの機能が長らくありませんでした。

実際には、GCCとClangには__attribute__((cleanup))という拡張機能がありました。これは変数のスコープ終了時にクリーンアップ関数を呼び出す仕組みです。さらに、10年以上前から利用可能でした。たとえば、systemdやGLibなどの大規模プロジェクトで活用されています。

しかし、cleanup属性には課題もありました。具体的には、使い方が直感的ではありません。変数宣言時に属性を付ける必要があり、コードの可読性が低下します。そのため、より自然な構文としてdeferの標準化が議論されてきました。

C言語のdefer技術仕様(TS 25755)の現状

2025年2月のWG14会議でdeferの技術仕様が承認されました。つまり、C言語の標準にdeferが組み込まれる道筋ができたのです。なお、これはまだ正式な規格ではなく技術仕様(TS)の段階です。

具体的には、ブロックの終了時に指定した処理を実行する構文です。たとえば、ファイルを開いた直後にdeferで閉じる処理を書きます。したがって、エラー処理のどの分岐を通っても確実にファイルが閉じられます。さらに、複数のdeferは逆順に実行されます。つまり、後から登録したものが先に実行されるのです。

また、GCC 9以降とClang 22以降で実際に動作するワークアラウンドも存在します。特に、Jens Gustedtというプログラマーが実用的な実装を公開しています。しかし、コンパイラのネイティブサポートが完成するまでにはもう少し時間がかかります。とはいえ、方向性は確定しているので先取りして使い始める価値はあります。

cleanup属性を使った従来の後始末パターン

defer対応を待てない場合は、cleanup属性が現実的な選択肢です。そこで、基本的なパターンを紹介します。

まず、クリーンアップ関数を定義します。たとえば、int型ポインタを解放する関数です。次に、変数宣言時にこの関数を属性として指定します。すると、変数がスコープを抜けるときに自動的に解放されます。つまり、手動でfreeを呼ぶ必要がなくなるのです。

しかし、この方法にはいくつかの注意点があります。具体的には、クリーンアップ関数の引数はポインタのポインタになります。また、初期化されていない変数にcleanupを使うと未定義動作になります。さらに、setjmpやlongjmpとの組み合わせでは期待通りに動かない場合があります。したがって、使用時には慎重なテストが必要です。

なお、C++ではRAIIパターンが標準的な後始末の方法です。特に、スマートポインタやstd::unique_ptrを使えばメモリ管理は自動化されます。だからこそ、deferの需要は主にC言語側に集中しています。

deferとcleanupの実務的な使い分け

実務ではどちらを使うべきでしょうか。いくつかの判断基準があります。

まず、プロジェクトのコンパイラ要件を確認しましょう。たとえば、GCC 9以上が保証されていればcleanupは安心して使えます。また、C23以降を対象にできるプロジェクトなら、deferのTS実装を検討する価値があります。しかし、組み込み系ではコンパイラのバージョンが古いことも多いです。そのため、環境に応じた判断が必要です。

次に、チームの慣れ度も重要な要素です。つまり、cleanup属性に馴染みのないメンバーが多い場合は学習コストを考慮します。具体的には、マクロでラップして読みやすくする工夫が有効です。さらに、コードレビューで使い方の統一性を保つことも大切です。

加えて、既存コードとの整合性も見ましょう。特に、gotoベースのエラーハンドリングが既に確立されているプロジェクトもあります。実際、Linuxカーネルではgotoパターンが標準です。むしろ、そのパターンが成熟している場合は無理に変える必要はありません。とはいえ、新規プロジェクトではdeferやcleanupの採用を積極的に検討すべきです。

GCCとClangのdefer対応まとめ

CとC++のリソース管理は長年の課題です。しかし、deferの標準化によって状況は改善に向かっています。だからこそ、今のうちにcleanup属性やdeferの概念に慣れておくことをおすすめします。特に、後始末の漏れはセキュリティ上のリスクにもなります。まずは小さなユーティリティ関数から後始末の自動化を試してみてください。