extern inlineは何をしますか?


93

inlineはそれ自体がコンパイラへの提案であり、その裁量で関数をインライン化することもしないこともあり、リンク可能なオブジェクトコードも生成することを理解しています。

static inlineは同じことをすると思いますが(インライン化してもしなくても)、インライン化するとリンク可能なオブジェクトコードを生成しません(他のモジュールがリンクできないため)。

写真のどこにextern inline収まるのですか?

プリプロセッサマクロをインライン関数で置き換え、この関数をインライン化する必要があるとします(たとえば、呼び出し元に対して解決する必要がある__FILE____LINE__マクロを使用するため、この呼び出された関数ではないため)。つまり、関数がインライン化されない場合に備えて、コンパイラーまたはリンカーのエラーを表示します。extern inlineこれを行いますか?(そうでない場合は、マクロを使用する以外にこの動作を実現する方法はないと思います。)

C ++とCの間に違いはありますか?

コンパイラのベンダーとバージョンの違いはありますか?

回答:


129

K&R CまたはC89では、インラインは言語の一部ではありませんでした。多くのコンパイラはそれを拡張機能として実装しましたが、それがどのように機能するかに関して定義されたセマンティクスはありませんでした。GCCはインライン実装する最初の中で、導入inlinestatic inlineおよびextern inline構築物。ほとんどのC99より前のコンパイラは、一般的にそのリードに従います。

GNU89:

  • inline:関数がインライン化されている可能性があります(ただし、これは単なるヒントです)。行外のバージョンは常に出力され、外部に表示されます。したがって、そのようなインラインは1つのコンパイルユニットでのみ定義でき、他のすべてはそれを行外関数として見る必要があります(または、リンク時に重複したシンボルを取得します)。
  • extern inline は、外れバージョンを生成しませんが、それを呼び出す可能性があります(したがって、他のコンパイルユニットで定義する必要があります。ただし、1定義の規則が適用されます。外れバージョンは、コンパイラが代わりにそれを呼び出す場合に備えて、ここにインラインで提供されます。
  • static inlineファイルの静的なバージョンを生成する可能性がありますが、外部から見えるアウトオブラインバージョンを生成しません。1つの定義のルールは適用されません。外部シンボルが発行されたり、外部シンボルが呼び出されたりすることはないためです。

C99(またはGNU99):

  • inline:GNU89のような "extern inline"; 外部から見える関数は発行されませんが、呼び出される可能性があるため存在する必要があります
  • extern inline:GNU89 "inline"のように:外部から見えるコードが発行されるので、多くても1つの翻訳単位でこれを使用できます。
  • static inline:GNU89の「静的インライン」のように。これは、gnu89とc99の間で唯一の移植可能なものです。

C ++:

どこでもインラインである関数は、同じ定義でどこでもインラインでなければなりません。コンパイラー/リンカーは、シンボルの複数のインスタンスを分類します。static inlineまたはの定義はありませんがextern inline、多くのコンパイラーには定義されています(通常はgnu89モデルに従います)。


2
クラシッククラシックCでは、「インライン」はキーワードではありませんでした。変数名として使用できました。これは、C89およびプレ標準(K&R)Cに適用される
ジョナサン・レフラー

あなたは正しいと思われます。修繕。(K&Rではなく)C89でキーワードとして予約されていたと思いましたが、覚えていないようです
puetzk

これをMicrosoftのVisual C ++に追加したいのですが、関数のインライン化を強制する__forceinlineキーワードがあります。これは明らかに、VC ++専用のコンパイラ固有の拡張機能です。
untitled8468927 2014年

C99の「extern inline」と指定子がまったくないことの違いはありますか?
Jo So

意味的にはありません。非インライン関数と同じようextern inlineに、1つの定義ルールが適用されます。これが定義です。ただし、実装定義の最適化ヒューリスティックinlineが「関数の呼び出しをできるだけ速くする」という提案としてキーワードを使用するという推奨に従う場合(ISO 9899:1999§6.7.4(5)、extern inlineカウント
puetzk

31

次のステートメントに基づいて、__ FILE__と__LINE__を誤解していると思います。

__FILE__マクロと__LINE__マクロを使用しているため、これらのマクロは呼び出し元で解決されますが、この呼び出された関数では解決されません。

コンパイルにはいくつかのフェーズがあり、前処理が最初です。__FILE__と__LINE__はそのフェーズで置き換えられます。そのため、コンパイラーはインライン化する関数を検討できるようになるまでに、すでに置き換えられています。


14

次のようなものを書こうとしているようです:

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

毎回異なる値が出力されることを期待しています。Donが言うように、__ FILE__と__LINE__はプリプロセッサによって実装されていますが、インラインはコンパイラによって実装されているため、そうはしません。そのため、printLocationをどこから呼び出しても、同じ結果が得られます。

唯一あなたが仕事にこれを取得することができる方法はprintLocationマクロにすることです。(はい、知っています...)

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

18
一般的なトリックは、マクロPRINT_LOCATIONが関数printLocationを呼び出し、FILEおよびLINEをパラメーターとして渡すことです。これにより、関数本体が自明ではない場合に、デバッガ/エディタ/その他の動作が改善される可能性があります。
スティーブジェソップ

@Roddy私のソリューションを見てください-あなたの拡張ですが、より包括的で拡張可能です。
enthusiasticgeek

@SteveJessop私が以下のソリューションにリストしたようなものですか?
enthusiasticgeek

3

inline、static inline、extern inlineの状況は複雑です。これは、gccとC99がそれらの動作に対してわずかに異なる意味を定義しているためです(おそらくC ++も同様です)。C 彼らが何をしているかについてのいくつかの有用で詳細な情報はここにあります


2

ここでは、インライン関数ではなくマクロを選択します。マクロがインライン関数を支配するまれな機会。次のことを試してください。この「MACRO MAGIC」コードを書いたところ、うまくいくはずです。gcc / g ++ Ubuntu 10.04でテスト済み

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)

#ifdef __cplusplus

#include <cstdio>
#include <cstring>

#else

#include <stdio.h>
#include <string.h>

#endif

//=========== MACRO MAGIC BEGINS ============

//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )

#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))

#define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);

//=========== MACRO MAGIC ENDS ============

int main (int argc, char** argv) {

  LOG("Greetings StackOverflow! - from enthusiasticgeek\n");

  return 0;
}

複数のファイルの場合、これらのマクロを、各c / cc / cxx / cppファイルに同じものを含む個別のヘッダーファイルで定義します。可能な限り、マクロよりもインライン関数またはconst識別子(必要に応じて)を優先してください。


2

「それは何をするのか」と答えるのではなく、「どうすれば自分のやりたいようにするのか」と答えます。5種類のインライン化があり、すべてGNU C89、標準C99、およびC ++で使用できます。

アドレスが取得されない限り、常にインライン

追加 __attribute__((always_inline))宣言にし、次のいずれかのケースを使用して、アドレスが取得される可能性を処理します。

そのセマンティクスが必要でない限り、これを使用しないでください(たとえば、特定の方法でアセンブリに影響を与えるため、または使用するため) alloca)。コンパイラは通常、それが価値があるかどうかをあなたよりよく知っています。

インラインで弱いシンボルを出力します(C ++のように、「単に動作させる」)

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

これにより、同じコードのコピーが多数残され、リンカが任意にコピーを選択することに注意してください。

インラインですが、シンボルを放出しません(外部参照は残します)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

常に放出する(1つのTUに対して、前のものを解決するため)

ヒント付きバージョンは、C ++では弱いシンボルを放出しますが、Cのいずれかの方言では強いシンボルを放出します。

void foo(void);
inline void foo(void) { ... }

または、両方の言語で強いシンボルを発するヒントなしでそれを行うことができます:

void foo(void) { ... }

一般に、定義を提供するときにTUがどの言語であるかを知っているので、おそらくそれほどインライン化する必要はありません。

インラインで、すべてのTUで放出

static inline void foo(void) { ... }

これらを除いて、上記staticvoid foo(void)宣言を追加できます。これは、クリーンなヘッダーを記述#includeし、インライン定義を含む別のファイルを作成する「ベストプラクティス」に役立ちます。次に、Cスタイルのインラインを使用する場合、#defineは、1つの専用TUで異なるマクロを使用して、範囲外の定義を提供します。

extern "C"ヘッダーがCとC ++の両方から使用される可能性がある場合は忘れないでください!


行方不明:MSVCはどうですか?C89方言拡張機能がいくつかありますが、MSVCを使用したことがなく、nm同等の機能を実行する方法がわかりません。
o11c 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.