PR

 C/C++には,ソースコード・レベルでプログラムを書き換える機能「マクロ」が備わっている。単純に言えば,コンパイル前にソースコード内の文字列を置き換える機能だ。まとまった処理にあらかじめ名前を付けておくと,実際にその処理が必要な個所には名前を記述するだけで,処理内容が展開された状態でプログラムがコンパイルされる。

 マクロを上手に使うことで,長く複雑なソースコードや間違えやすい記述を端的な言葉に置き換え,ソースを読みやすくできる。一方で,単純な置き換えであるがゆえの問題点もあったりする。

 今回は,便利なマクロの機能について調査した。

「マクロ」と言えばExcel…?!

 マクロ(macro)とは,本来「大きな・巨視的な・大局的な」という意味の形容詞だが(対義語は「ミクロ(micro)」),コンピュータ用語ではこれが名詞となり,「一定の処理に名前を付けたもの」という意味を持つことになった。

 マクロと聞いて,より多くの人が思い浮かべるのは,表計算ソフトExcelのマクロ機能だろう。Excelのマクロは,ユーザーの操作を記録して名前を付けて保存する機能だ。マクロとして保存した一連の操作は,その名前を指定することで何度でも繰り返して実行できる。Excelのマクロの実体はVBA(Visual Basic for Applications)のプログラムであり,Visual Basic Editorを使って細部を修正できることはご存じの通りだ。

#defineで定義し,プリプロセサが「展開」する

 C/C++に搭載されているマクロも,まとまった処理に名前を付けて簡単に再利用できるという点で,これらオフィス・アプリケーションのマクロと同じ意味合いの機能だ。

 C/C++では,#defineプリプロセサ指令でマクロを定義する。プリプロセサ指令で記述することからもわかるように,C/C++のマクロは,機械語への翻訳が始まる前の「プリプロセス(前処理)」の段階で処理される*1

 #define指令の基本的な書式は,

 #define 文字列1 文字列2

という形である。このようにマクロを定義すると,文字列2に対して文字列1という名前(別名)が与えられ,この指令以降,ソースコードに現れる文字列1は,すべて文字列2に置き換わった状態でコンパイルされる。この動作を「マクロ展開」と呼ぶ。

 「まとまった処理に名前を付ける」というマクロの役割は,関数とも似ている。実際,先述したExcelのマクロは,VBAで作ったユーザー定義関数そのものである。C/C++でも,引数付きで定義できる「マクロ関数」と呼ぶマクロを定義できる(文末記事「#define指令による記号定数の定義」を参照)。ただし,C/C++のマクロには,関数とは大きく異なる側面がある。

 C/C++の関数は,コンパイル後もその名前を持ったコードのかたまりとして実体を持つ。実行する際には,関数名を示すことでその実体のアドレスへと処理が移行し(呼び出し),関数内での処理が終わると元の流れに移行する(戻る)――という形になる(図1)。一方,マクロは,ソースコード中のコード群を別名で置き換える文字列置換の機能である。名前で示されたソースそのものが,コード中にその都度展開(置換)される(図2)。コンパイルされるのは展開後のコードなので,コンパイル後のコードにはマクロ名で参照できるような実体は存在しない。

図1●関数の呼び出し
図1●関数の呼び出し
図2●マクロの展開
図2●マクロの展開