web-dev-qa-db-ja.com

静的か非静的か? (非OOP機能付き)

この質問は以前に出てきましたが、私のものは異なります何もカバーすることは想定されていないためOOPしかし- プレーン(ANSI)C

特にオープンソースソフトウェアでは、通常、大量の関数が発生します(OOPではnotであるため、メソッドを呼び出さないでください)。データ型の前にあるstaticキーワード。

しかし、これがプレーンCに実際にどのような影響を与えるのでしょうか。私自身のプロジェクトでは、staticキーワードが実際に必要になる機会はありませんでした。 (いわゆるエキスパートプログラマーからおそらく「この関数は静的に宣言する必要があります」というアドバイスがあったでしょうが、私はおそらく自分自身の皮肉な方法で答えたでしょう:「ええと、それはそのままで動作しますすべて"。 :))。

OSSコードの読み取りから、プロジェクトをコンパイルするときにオーバーヘッドが少なくなる可能性があると思いました。ただし、CPUのパフォーマンスは非常に進歩しているため、実行速度の違いを測定するためのベンチマークツールが必要な場合でも、関数をstaticと宣言することへの関心が高まっている理由は何度もありました。

5
syntaxerror

「静的」はCで2つのことを意味するためにオーバーロードされています。

  • ファイルレベルでは、スコープを現在のファイルに制限します。 (これはあなたが見ているものだと思います。)

  • 関数内では、呼び出し間で値が保持されるように変数を変換します。

「静的」は、Cでのパフォーマンスとはほとんど関係ありません。これが使用されているのを見たコンテキストでは、アクセスの範囲に関連しています。この使用法では、「静的」は実際には、C++メソッドに適用される「静的」キーワードよりもOOP言語の修飾子にアクセスする機能と似ています。

18
user1172763

静的とは、関数に適用される場合、関数にローカルスコープまたはファイルスコープがあることを意味します。これにより、誰かがヘッダーファイルでその関数のプロトタイプを宣言し、宣言された場所以外の場所で使用することができなくなります。

これはパフォーマンスに影響を与えませんが、それでも良い方法です。どうして?スコープが狭くなるからです。プログラマーがstatic関数を見つけた場合、その関数は他の未知の場所では使用されないことを示しています。したがって、その関数を壊した場合、少なくとも損傷はソースに限定されているはずです。問題のファイル。

ただし、staticは、誰かが関数へのポインターを取得してそのポインターを外部化できるため、が宣言されたファイルの外部で関数を呼び出すことができないという保証はありません。

6
glampert

staticと宣言されていないCプログラムのすべての関数または変数には、一意の名前を付ける必要があります。システムの異なる部分を作成する2人以上のプログラマーがそれぞれ「fred」と呼ばれる関数を書き込み、両方の関数が静的であると宣言されている場合、2つの「fred」関数があるという事実はまったく無関係であり、問​​題を引き起こしません) 。関数の1つを除くすべてが静的である場合、問題はない可能性がありますが、プログラマーがextern宣言を1つの関数を参照することを意図してどこかに追加した場合でも、問題が発生する可能性がありますが、目的の関数は静的であり、宣言は他の場所で宣言された非静的関数に実際にバインドします。静的ではない「fred」関数が2つ以上ある場合、問題はほぼ保証されています。ほとんどの場合、リンカーはプログラムを拒否します。場合によっては(たとえば、メソッドの1つが「弱い」属性でタグ付けされている場合)、リンカは動作しませんが、1つのメソッドを呼び出すことを意図していたコードが他のメソッドを呼び出すことがあります。両方の方法が同じように動作する場合、問題は発生しない可能性がありますが、方法が異なる場合、そのような置換は悲惨なものになる可能性があります。

プログラマがデフォルトでメソッドを非静的にする場合、名前の衝突が発生する可能性があります。デフォルトで物事を静的にすることでその問題を回避できます。

4
supercat

関数定義に適用すると、staticキーワードは関数名がリンカーにエクスポートされないようにします。他の翻訳単位(ファイル)のコードは、その関数を名前で呼び出すことはできません。

いくつかの用語を悪用するために、関数定義をそのソースファイルに対して「プライベート」にします。

たとえば、次のようなファイルがあるとします。

static int a_helper_function( /* some arguments here */ )
{
  ...
}

int public_function( /* some arguments here */ )
{
  ...
  if ( a_helper_function( /* arguments */ ))
    /* do something interesting */
  ...
}

プロジェクトをビルドすると、同じソースファイル内で定義された関数のみがa_helper_functionを直接呼び出すことができます。他の翻訳単位のコードからは見えません。

これは、メンテナンスや設計の問題ほどのパフォーマンスの問題ではありません。名前空間の競合が軽減され(a_helper_functionの複数のバージョンが異なるファイルに異なる署名を持つ可能性があります)、各ファイル内のいくつかの操作をカプセル化できます。

これで、関数名がリンカーにエクスポートされなくなります。他の翻訳単位は、名前ではなく、この関数を呼び出すことができます。同じファイルにa_helper_functionへのポインタを返す別の関数を含めることができます。

int (*get_helper(/* args for get_helper */))(/* args for a_helper_function */)
{
  return a_helper_function;
}

get_helper関数はa_helper_functionへのポインターを返すため、別の変換単位の関数はその関数ポインターを使用してa_helper_functionを呼び出すことができます。

2
John Bode