Level 3:関数 3.8 インライン関数

※資料は自著より引用


■3.8 インライン関数

・スタック

 スタックの解説についてはこちらを参照のこと。


・システムスタック

前期で学習したように,現代のプログラムのほとんどすべてで,関数・メソッド・サブルーチン・プロシージャ
などと呼ばれるものを呼び出して利用する際に,メモリの一部をスタックとして使っている。これを一般に
システムスタックと呼ぶ。

 ●関数の呼び出し連鎖の仕組み
 ●メモリ上での関数呼び出しの様子
 ●システムスタックの変化の様子

について,下図に示す。







上記の様に,関数を呼び出す際に形成されるスタック上の枠組み(戻りアドレス領域,仮引数領域,返値領域,局所変数領域)
スタックフレームと呼ぶ。

つまり,関数を呼び出すときには
 (1)関数を呼び出す準備としてスタックに戻り番地をプッシュし,仮引数領域と返値領域を確保(SPの移動)してスタックフレームを形成する
 (2)スタックフレーム上仮引数領域に実引数値をコピーする(仮引数の実引数による初期化)
 (3)関数本来の処理を実行する
 (4)返値があれば返値をスタックフレーム上の返値領域に代入する
 (5)スタックフレームを解放し(SPの移動),戻り番地をポップして関数の呼び出し直後に戻る
という具合に動作しており,本来の処理以外にも(1),(2),(4)(5)のような準備・後始末的処理を実行している。
この関数を呼び出す際の準備・後始末的処理,およびその実行に掛かる時間をオーバーヘッドと呼ぶ。


●インライン関数

 インライン関数とは,見た目は普通に関数を呼び出しているように見えても,実際には,
関数化された処理(コード)を,関数呼び出し位置にそのまま展開(インライン展開などと呼
ぶ。)するような関数である。

たとえば,

 inline int add( int a, int b ) { return a + b; }

というインライン関数が定義されているとして,このインライン関数を以下の様に呼び出すとする。

int main( void ) {

 int x = 10, y = 20, z;

 z = add( x, y );

 return 0;
}

すると,実際にはインライン関数addの処理がその場に以下の様に展開される(実際には機械語の
レベルで展開される)。

int main( void ) {

 int x = 10, y = 20, z;

 int a = x;
 int b = y;

 int temp = a + b;
 z = temp;

 return 0;
}


インライン関数の定義方法を以下に紹介する。




どのような関数をインライン関数に指定すべきか。




インライン関数の大元のアイディアは,プリプロセッサの引数付きマクロで擬似的な関数を定義するテクニックである。

例)

 # define add( a, b ) ((a) + (b))

と関数に近い形で定義しておき,

 z = add( x, y );

と使用すると,プリプロセッサによって

 z = ((x) + (y));

とマクロ展開される。効果的には,インライン関数と同じと言って良い(コードが使用場所に展開されるので関数呼び
出しのオーバーヘッドが生じない)。

C++はCからの進化版として,「できるだけプリプロセッサの力を使わずにプログラミングできるように」 設計されて
おり,インライン関数も,上記のテクニックをプリプロセッサの力を使わずに利用できる様に文法でサポートしたもの
と言える。

なお,

 ・再帰的な呼び出しを行う関数(自分自身を処理の中で呼び出す)関数は インライン指定されてもインライン関数にはならない。

これは,再帰的な呼び出しはスタックフレームを使った呼び出し機構を利用しているからである。

また,

 ・インライン関数への関数ポインタは利用できる。

これは,インライン版と非インライン版の2通りのバージョンが生成され,関数へのポインタが必要な場合
は,非インライン版関数へのポインタが使用されるためである。