web-dev-qa-db-ja.com

C / C ++:ヘッダーファイルの静的関数、それはどういう意味ですか?

静的関数がソースファイルで宣言されているときの意味を知っています。私はいくつかのコードを読んでいますが、ヘッダーファイルの静的関数は他のファイルで呼び出すことができます。

49
Jarod

関数はヘッダーファイルで定義されていますか?そのため、実際のコードは次のように関数に直接与えられます。

_static int addTwo(int x)
{
  return x + 2;
}
_

それは、多くの異なるCファイルに便利な機能を提供する方法にすぎません。ヘッダーを含む各Cファイルは、呼び出し可能な独自の定義を取得します。もちろん、これはメモリを浪費し、(私の意見では)実行するのは非常にいことです。なぜなら、ヘッダーに実行可能コードを含めることは一般に良い考えではないからです。

_#include_ ::ヘッダーを追加すると、基本的にはヘッダー(およびヘッダーに含まれる他のヘッダー)の内容がコンパイラーから見たCファイルに貼り付けられるだけです。コンパイラは、特定の1つの関数定義がヘッダーファイルからのものであることを決して知りません。

[〜#〜] update [〜#〜]:多くの場合、実際には上記のようなことを行うことをお勧めします。これについての答えは非常に白黒に聞こえますが、これは少し単純化しすぎているようなものです。たとえば、 intrinsic functions をモデル化する(または単に使用する)コードは、上記のように、明示的なinlineキーワードでも表現できます。

_static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}
_

ここでは、__add_two_superquickly()関数は架空の組み込み関数であり、関数全体を基本的に1つの命令にコンパイルするため、インライン化することを本当に望んでいます。それでも、上記はマクロを使用するよりも簡潔です。

もちろん、組み込み関数を直接使用することの利点は、別の抽象化層にラップすることにより、代替実装を提供し、使用するコンパイラに応じて適切なものを選択することにより、特定の組み込み関数がないコンパイラーでコードを構築できることです。

61
unwind

含まれるすべてのcppファイル内に、同じ名前の個別の静的関数を効果的に作成します。同じことがグローバル変数にも当てはまります。

14
sharptooth

他の人が言っているように、.cファイル自体のstatic関数とまったく同じ意味を持ちます。これは、.cファイルと.hファイルの間に意味的な違いがないためです。 .c行(通常は#include)という名前のすべてのファイルの内容を持つ、実際にコンパイラーに渡されるファイル(通常は[.h]という名前)で構成されるコンパイル単位のみがあります。プリプロセッサから見たときにストリームに挿入されます。

Cソースが.cという名前のファイルにあり、パブリック宣言が.hという名前のファイルにあるという規則は単なる規則です。しかし、一般的には良いものです。この規約では、.hファイルに表示されるのは宣言のみであるため、通常、単一のプログラムで同じシンボルdefinedを複数回使用することは避けます。

この特定のケースでは、staticキーワードはシンボルをモジュールに対してプライベートにするため、トラブルを引き起こすのを待っている複数の定義の競合はありません。その意味で、そうすることは安全です。しかし、関数がインライン化されるという保証がない場合、コードセグメントのメモリの浪費である#includeそのヘッダーファイルに発生したすべてのモジュールで関数がインスタンス化されるというリスクがあります。 。

一般的に利用可能な公開ヘッダーでこれを行うことを正当化するユースケースが何であるかはわかりません。

.hファイルが生成されたコードであり、単一の.cファイルにのみ含まれている場合、実際にパブリックヘッダーではないことを強調するために、.h以外のファイルに個人的に名前を付けますまったく。たとえば、バイナリファイルを初期化された変数定義に変換するユーティリティは、#includeを介して使用することを目的としたファイルを作成し、変数のstatic宣言を含めることができます。アクセサまたはその他の関連ユーティリティ関数のstatic定義まで。

7
RBerteig

関数をヘッダーファイルで定義する(単純に宣言するのではなく)場合、関数のコピーが各変換単位(基本的にこのヘッダーを含む各cppファイル)で生成されます。

これにより、実行可能ファイルのサイズが大きくなる場合がありますが、関数が小さい場合は無視できる場合があります。利点は、ほとんどのコンパイラーが関数をインライン化できるため、コードのパフォーマンスが向上する可能性があることです。

しかし、これを行うにはbigの違いがありますが、これはどの回答でも言及されていません。関数が次のような静的ローカル変数を使用する場合:

static int counter()
{
    static int ctr = 0;
    return ctr++;
}

のではなく:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}

このヘッダーを含む各ソースファイルには、独自のカウンターがあります。関数がヘッダー内で宣言され、ソースファイルで定義されている場合、カウンターはプログラム全体で共有されます。

だから、唯一の違いはパフォーマンスとコードサイズが間違っているということです。

4
galinette

小さなインライン関数を持ついくつかの「ヘッダー専用」ライブラリで役立ちます。そのような場合、これは悪いパターンではないため、常に関数のコピーを作成する必要があります。ただし、これにより、単一のヘッダーファイルに個別のインターフェイス部分と実装部分を簡単に挿入できます。

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

GLKフレームワークのAppleベクトル数学ライブラリは、このような構造を使用します(例:GLKMatrix4.h)。

1
mawigator

ソースファイルまたはヘッダーファイルでの定義に意味的な違いはありません。静的キーワードを使用する場合、基本的には両方ともプレーンCで同じことを意味し、スコープを制限しています。

ただし、これをヘッダーファイルに書き込むには問題があります。これは、ソースファイルにヘッダーを含めるたびに、ヘッダーに通常の関数が定義されているのと非常によく似た同じ実装の関数のコピーがあるためですファイル。ヘッダーに定義を追加すると、静的関数の目的が達成されません。

したがって、ヘッダーではなくソースファイルのみに実装を含めることをお勧めします。

1
legend2804