まず、コンパイラは、inline
;としてマークされた関数を常にインライン化するとは限りません。たとえば、すべての最適化をオフにすると、おそらくインライン化されません。
インライン関数を定義する場合
inline void do_something(void)
{
blah
}
その関数を使用すると、同じファイル内であっても、その関数の呼び出しは、暗黙的に「extern」であるため、コンパイラではなくリンカによって解決されます。ただし、この定義だけでは、関数の外部定義は提供されません。
なしで宣言を含める場合inline
void do_something(void);
定義を見ることができるCファイルではinline
、コンパイラは関数の外部定義を提供し、エラーはなくなるはずです。
動作する理由static inline
は、関数がそのコンパイルユニット内でのみ表示されるため、コンパイラが関数の呼び出しを解決(および最適化)して、そのコンパイルユニット内の関数のコードを出力できるようにするためです。リンカはそれを解決する必要がないので、外部定義は必要ありません。
インライン関数を配置するのに最適な場所は、ヘッダーファイルにあり、それらを宣言しますstatic inline
。これにより、外部定義が不要になるため、リンカーの問題が解決されます。ただし、これにより、コンパイラーは、それを使用するすべてのコンパイル単位で関数のコードを発行するため、コードが膨張する可能性があります。ただし、関数はインラインであるため、とにかく小さい可能性があります。通常、これは問題になりません。
もう1つのオプションは、ヘッダーのように定義し、1つのCファイルで修飾子なしで宣言を提供することです。extern inline
extern
inline
gccマニュアルはそれをこう説明しています:
関数をインラインで宣言することにより、GCCにその関数をより速く呼び出すように指示できます。GCCがこれを実現する1つの方法は、その関数のコードを呼び出し元のコードに統合することです。これにより、関数呼び出しのオーバーヘッドが排除され、実行が高速化されます。さらに、実際の引数値のいずれかが一定である場合、それらの既知の値により、コンパイル時に簡略化できる可能性があるため、インライン関数のコードのすべてを含める必要はありません。コードサイズへの影響はあまり予測できません。オブジェクトコードは、特定のケースに応じて、関数のインライン化によって大きくなったり小さくなったりする場合があります。オプションを使用して、すべての「十分に単純な」関数を呼び出し元に統合するようにGCCに指示することもできます-finline-functions
。
GCCは、関数をインラインで宣言する3つの異なるセマンティクスを実装しています。1つは属性がすべてのインライン宣言に存在する場合-std=gnu89
または存在-fgnu89-inline
する場合に使用でき、もう1つは、、、または
(なし)の場合に使用でき、3つ目はC++のコンパイル時に使用されます。gnu_inline
-std=c99
-std=c1x
-std=gnu99
-std=gnu1x
-fgnu89-inline
関数をインラインで宣言するにはinline
、次のように宣言でキーワードを使用します。
static inline int
inc (int *a)
{
return (*a)++;
}
ISO C90プログラムにインクルードするヘッダーファイルを作成する場合は、__inline__
の代わりに作成してinline
ください。
3種類のインライン化は、上記の例のように関数で
inline
キーワードが使用された場合と、キーワードを使用せずに関数が最初に宣言され、次に次のように定義された場合の2つの重要な場合で同様に動作します。static
inline
inline
extern int inc (int *a);
inline int
inc (int *a)
{
return (*a)++;
}
inline
これらの一般的なケースの両方で、プログラムは、速度を除いて、キーワードを使用しなかった場合と同じように動作します。
関数がインラインとstatic
の両方である場合、関数へのすべての呼び出しが呼び出し元に統合され、関数のアドレスが使用されない場合、関数自体のアセンブラーコードが参照されることはありません。この場合、オプションを指定しない限り、GCCは実際には関数のアセンブラコードを出力しません
-fkeep-inline-functions
。一部の呼び出しは、さまざまな理由で統合できません(特に、関数の定義の前にある呼び出しは統合できず、定義内の再帰呼び出しも統合できません)。統合されていない呼び出しがある場合、関数は通常どおりアセンブラコードにコンパイルされます。プログラムがそのアドレスを参照する場合も、インライン化できないため、関数は通常どおりにコンパイルする必要があります。
関数定義での特定の使用法により、インライン置換に適さなくなる可能性があることに注意してください。これらの使用法には、varargsの使用、allocaの使用、可変サイズのデータ型の使用、計算されたgotoの使用、非ローカルgotoの使用、および入れ子関数があります。を使用すると、マークされた関数を置き換えることができなかっ-Winline
た場合に警告が表示され、失敗の理由が示されます。inline
inline
ISO C ++で要求されているように、GCCは、キーワードで明示的に宣言されていない場合でも、クラスの本体内で定義されたメンバー関数がインラインとしてマークされていると見なします。これは。でオーバーライドできます-fno-default-inline
。
always_inline
次のように関数の属性を指定しない限り、GCCは最適化しないときに関数をインライン化しません。
/* Prototype. */
inline void foo (const char) __attribute__((always_inline));
このセクションの残りの部分は、GNUC90インライン化に固有のものです。
インライン関数がそうでないstatic
場合、コンパイラは他のソースファイルからの呼び出しがある可能性があると想定する必要があります。グローバルシンボルはどのプログラムでも一度しか定義できないため、他のソースファイルで関数を定義してはならず、その中の呼び出しを統合することはできません。したがって、非static
インライン関数は常に通常の方法で単独でコンパイルされます。
関数定義でinline
との両方を指定すると、定義はインライン化にのみ使用されます。extern
アドレスを明示的に参照している場合でも、関数が単独でコンパイルされることはありません。このようなアドレスは、関数を宣言しただけで定義しなかったかのように、外部参照になります。
とのこの組み合わせはinline
、extern
ほとんどマクロの効果があります。これを使用する方法は、これらのキーワードを使用して関数定義をヘッダーファイルに配置し、定義の別のコピー(不足しているinline
およびextern
)をライブラリファイルに配置することです。ヘッダーファイルの定義により、関数へのほとんどの呼び出しがインライン化されます。関数の使用が残っている場合は、ライブラリ内の単一のコピーを参照します。