6

一部のマクロサブルーチンをインライン関数に置き換えようとしているので、コンパイラーはそれらを最適化できるので、デバッガーはそれらにステップインできます。通常の関数として定義すると、次のように機能します。

void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

しかし、それらをインラインとして定義すると、次のようになります。

inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

「エラー:未定義の外部」と表示されます。どういう意味ですか?暗闇の中で刺してみました

static inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

これ以上のエラーはありません。関数の定義と関数の呼び出しは、同じ.cファイルにあります。

誰かがなぜ一方が機能し、もう一方が機能しないのか説明できますか?

(2番目の関連する質問:複数の.cファイルでインライン関数を使用する場合、どこにインライン関数を配置しますか?)

4

3 に答える 3

10

まず、コンパイラは、inline;としてマークされた関数を常にインライン化するとは限りません。たとえば、すべての最適化をオフにすると、おそらくインライン化されません。

インライン関数を定義する場合

inline void do_something(void)
{
  blah
}

その関数を使用すると、同じファイル内であっても、その関数の呼び出しは、暗黙的に「extern」であるため、コンパイラではなくリンカによって解決されます。ただし、この定義だけでは、関数の外部定義は提供されません。

なしで宣言を含める場合inline

void do_something(void);

定義を見ることができるCファイルではinline 、コンパイラは関数の外部定義を提供し、エラーはなくなるはずです。

動作する理由static inlineは、関数がそのコンパイルユニット内でのみ表示されるため、コンパイラが関数の呼び出しを解決(および最適化)して、そのコンパイルユニット内の関数のコードを出力できるようにするためです。リンカはそれを解決する必要がないので、外部定義は必要ありません。

インライン関数を配置するのに最適な場所は、ヘッダーファイルにあり、それらを宣言しますstatic inline。これにより、外部定義が不要になるため、リンカーの問題が解決されます。ただし、これにより、コンパイラーは、それを使用するすべてのコンパイル単位で関数のコードを発行するため、コードが膨張する可能性があります。ただし、関数はインラインであるため、とにかく小さい可能性があります。通常、これは問題になりません。

もう1つのオプションは、ヘッダーのように定義し、1つのCファイルで修飾子なしで宣言を提供することです。extern inlineextern 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つの重要な場合で同様に動作します。staticinlineinline

 extern int inc (int *a);
 inline int
 inc (int *a)
 {
   return (*a)++;
 }

inlineこれらの一般的なケースの両方で、プログラムは、速度を除いて、キーワードを使用しなかった場合と同じように動作します。

関数がインラインとstaticの両方である場合、関数へのすべての呼び出しが呼び出し元に統合され、関数のアドレスが使用されない場合、関数自体のアセンブラーコードが参照されることはありません。この場合、オプションを指定しない限り、GCCは実際には関数のアセンブラコードを出力しません -fkeep-inline-functions。一部の呼び出しは、さまざまな理由で統合できません(特に、関数の定義の前にある呼び出しは統合できず、定義内の再帰呼び出しも統合できません)。統合されていない呼び出しがある場合、関数は通常どおりアセンブラコードにコンパイルされます。プログラムがそのアドレスを参照する場合も、インライン化できないため、関数は通常どおりにコンパイルする必要があります。

関数定義での特定の使用法により、インライン置換に適さなくなる可能性があることに注意してください。これらの使用法には、varargsの使用、allocaの使用、可変サイズのデータ​​型の使用、計算されたgotoの使用、非ローカルgotoの使用、および入れ子関数があります。を使用すると、マークされた関数を置き換えることができなかっ-Winlineた場合に警告が表示され、失敗の理由が示されます。inline

inlineISO C ++で要求されているように、GCCは、キーワードで明示的に宣言されていない場合でも、クラスの本体内で定義されたメンバー関数がインラインとしてマークされていると見なします。これは。でオーバーライドできます-fno-default-inline

always_inline次のように関数の属性を指定しない限り、GCCは最適化しないときに関数をインライン化しません。

 /* Prototype.  */
 inline void foo (const char) __attribute__((always_inline));

このセクションの残りの部分は、GNUC90インライン化に固有のものです。

インライン関数がそうでないstatic場合、コンパイラは他のソースファイルからの呼び出しがある可能性があると想定する必要があります。グローバルシンボルはどのプログラムでも一度しか定義できないため、他のソースファイルで関数を定義してはならず、その中の呼び出しを統合することはできません。したがって、非staticインライン関数は常に通常の方法で単独でコンパイルされます。

関数定義でinlineとの両方を指定すると、定義はインライン化にのみ使用されます。externアドレスを明示的に参照している場合でも、関数が単独でコンパイルされることはありません。このようなアドレスは、関数を宣言しただけで定義しなかったかのように、外部参照になります。

とのこの組み合わせはinlineexternほとんどマクロの効果があります。これを使用する方法は、これらのキーワードを使用して関数定義をヘッダーファイルに配置し、定義の別のコピー(不足しているinlineおよびextern)をライブラリファイルに配置することです。ヘッダーファイルの定義により、関数へのほとんどの呼び出しがインライン化されます。関数の使用が残っている場合は、ライブラリ内の単一のコピーを参照します。

于 2012-02-22T17:07:59.363 に答える
1

関数がC99で動作するためinlineには(それらは言語にのみ登場しました)、ヘッダーファイルで定義を指定する必要があります

inline void do_something(void)
{
  blah
}

1つのコンパイルユニット(別名.c)に、ある種の「インスタンス化」を配置します

void do_something(void);

なしinline

于 2012-02-22T17:02:03.773 に答える
0

複数のファイルから使用する場合は、ヘッダーファイルに入れる必要があります。

リンカエラーの場合:関数のデフォルトの宣言は、それが「外部」であることを意味しますが、インライン化されているため、リンカはコンパイラによって生成されたシンボルスタブを見つけることができるため、エラーが発生します。

于 2012-02-22T16:43:47.110 に答える