Cのファイルスコープの「静的」変数は「extern」グローバル変数と同じくらい悪いのですか?


8

staticCでは、C ++でプライベートクラスメンバー変数を使用する場合に、(スタイルの問題として)ファイルスコープ変数を使用することがよくあります。マルチスレッドプログラムにスケーリングする場合thread_localは、C11または長くサポートされている拡張機能を追加__threadするだけで十分です。私はあなたが内部のすべてを置くことによってC ++とCで正確に同じことを行うことができます知っているstructし、それへのポインタをとる関数のセット作りstruct、その最初の引数としてを。一部のライブラリは、これを広範囲にわたって実行します。しかし、私の個人的なスタイルはstruct、必要に応じて、できるだけ小さくすることです。

「グローバル」変数が非常に悪いと主張する人々をよく読んだり聞いたりします。私はそれらの理由を追っていますが、彼らの議論のほとんどはexternC用語のグローバル変数に関連しているようです。彼らの言うことは確かに真実です。extern宣言された変数の1つまたは2つをプログラム全体で使用すると、処理が大幅に簡略化され、追跡が容易になる場合がありますが、さらに先に進むとプログラムが予測不可能になります。

何についてのstatic変数?彼らはまだ「実際の」グローバル変数と同じ問題を抱えていますか?多分私はこの質問をして私がしていることが正しいと思うなら続ける必要さえないかもしれませんが、今日私は別の「グローバル変数は悪い」種類の投稿を見た、そしておそらくこれはおそらく正しいとここに来ましたそのような質問のための場所。あなたの考えは何ですか?

この質問はの重複ではありません。このこの質問はについて尋ねているためexternstatic他の質問は、ファイル・スコープとブロックスコープについてです一方、非ローカル変数static変数。



3
これは重複しているとは思わない。この質問は、外部静的(グローバル)変数と比較したファイル静的変数について尋ねています。もう1つの問題は、関数の静的変数をグローバル変数と比較することです。

@gnatこれは重複した質問ではありません。私は、スノーマンがコメントで言ったことを基本的に言っていることを説明するために編集を加えました。タイトルも違う。
xiver77 2015

@ xiver77現在、重複して終了する投票はありませんが、他の質問に対する回答のいくつかは、ここで洞察に満ちている可能性があります。

@Snowman私の読書ごとに、他の質問が3つのケースすべてをカバーするという受け入れられた回答(ファイルスコープの静的が関数スコープとグローバルの両方よりも優先される理由と理由を説明しているようです)
gnat

回答:


17

適切に設計されたCプログラムでは、ファイル静的変数はクラスのプライベート静的メンバーに似ています。

  • これは、プライベート静的メンバー変数が、それが定義されているクラスの関数によってのみアクセスできる方法と同様に、そのファイル内の関数によってのみアクセスできます。

  • 変数のコピーは1つだけです。

  • その寿命はプログラムの寿命です。

extern変数は、それらをサポートする任意の言語のように真のグローバル変数になります。

static非グローバル変数がありませんグローバルとして悪いよう。実際には、それらが必要な場合もあります。

  • アクセスは、作成した関数を介して制御されます。これは、境界チェックとスレッドセーフの両方を含むデータの整合性に役立ちます。(注:これはスレッドの安全性を保証するものではありません。これは、途中で役立つ1つのツールにすぎません)

  • データはカプセル化されています。そのファイルだけがアクセスできます。これは、Cが複数の関数が静的変数にアクセスできるカプセル化に到達できるのと同じくらい近いです。

グローバル変数は何があっても悪いです。静的ファイル変数にはプライベート静的変数の利点がありますが、グローバル変数の欠点はありません。

唯一の問題は、C ++のような真のプライベート静的変数とは異なり、他のファイルは宣言にextern一致する変数を宣言でき、アクセスを防ぐことはできません。言い換えれば、それをグローバル変数に変換することを避けるために名誉システムに依存しています。


6

ファイルスコープ内または関数内のextern変数と非const static変数を含むグローバル状態は、特定の問題の簡単な解決策になることがよくありますが、3つの問題があります。

  1. static変数は置換できない依存関係になる傾向があるため、コードをテストできなくなりますstatic。または、よりOOP-yの言葉で言えば、依存関係の逆転の原則に従っていません。Perlなどの動的言語からCおよびC ++に来たので、私のコストモデルは仮想ディスパッチや関数ポインターなどに傾いています。現在の言語では、テスト容易性と優れたアーキテクチャーの間にいくつかの矛盾がありますが、依存関係を明示的にしてテストでそれらをオーバーライドできるようにするマイナーな迷惑は、テストを簡単に記述できることによって著しく相殺され、ソフトウェアが次のように機能していることを確認します期待された。コードをより動的にすることなく、テストの依存関係を挿入するために利用できる唯一のメカニズムは、条件付きコンパイルです。

  2. グローバルな状態は正確性についての推論を困難にし、それがバグにつながります。変数にアクセスできるビットやピースが多くなり、変数を変更できるほど、何が起こっているのか見失いやすくなります。代わりに、変数の単一割り当てを優先してください!好むconstところはどこでも合理的!正しさのチェックを導入できるゲッターとセッターを通じて変数を保護することをお勧めします。状態であるかどうかにかかわらstaticextern、それはまだ可能です正確さを維持するために、しかし、私は一週間の私が今の私ほどスマートではないと仮定する方が常に良いです。特にC ++では、クラスを使用して、何かを誤用することを不可能にするさまざまな抽象化をモデル化できます。そのため、インテリジェンスではなく型システムを利用してみてください。

  3. グローバル状態は、関数が再入可能でないこと、または一度に1つのコンテキストでのみ使用できることを意味している可能性があります。1つの接続しか管理できないデータベースドライバーを想像してみてください。それは完全に不必要な制限です。実際には、結果の集計に使用されるグローバル変数など、制限はしばしば微妙です。代わりに、データフローを明示的にして、すべてを関数パラメーターを介して渡します。繰り返しになりますが、C ++クラスはこれをより扱いやすくすることができます。

明らかに、static const NAMED_CONSTANTS大丈夫です。static関数の内部を使用するのはかなりトリッキーです。遅延して初期化された定数には役立ちますが、テストがかなり困難になる場合があります。妥協案は、静的変数から初期値の計算を分離して、両方の部分を個別にテストできるようにすることです。

小さな自己完結型プログラムでは、これらすべてが問題になることはなく、static状態を使い続けることができます。しかし、500 LOCを通過するか、再利用可能なライブラリを作成している場合は、不必要な制限なしに、優れたアーキテクチャと優れたインターフェースについて考え始める必要があります。


デザイン性と安全性に関するご提案のいくつかは、時々 Cで書かれる必要があるプログラムでの世話をすることは不可能である
xiver77

また、グローバルな状態は必ずしもスレッドにとって不親切ではありません。最新のCおよびC ++標準にはthread_local、標準化前の拡張機能として、コンパイラーが長い間サポートされていました。
xiver77 2015

あなたが言及したグローバルな状態の問題の多くを回避するために、よく書かれたCプログラムのほとんどは、外部依存関係が最小限に抑えられた、浅くて透過的な構造になっています。
xiver77 2015

バグが発生しやすい一方で、コードを静的にしてデータを変更可能に保つことは、現在のコンピュータアーキテクチャで可能な最も効率的なプログラムを作成する唯一の方法です。
xiver77 2015

@ xiver77私はより高レベルのコードを書く余裕があるので、パフォーマンスのためにアーキテクチャを犠牲にすることも、Cを使用することも、私の仕事では必要ありません:) Cを書くとき、正しいコードを書くことと、適切な抽象化の方法は私が好むものとは異なりますが、それでも多くの安全性を提供することは可能です。私の回答では、C ++固有のアイデアを明確に示しています。小さなファイルについてのあなたの主張は非常に良いです、それはプログラムについて推論するのをより簡単にし、static変数によって引き起こされる問題を減らしますが、排除しません。
2015

1

ファイルスコープの変数をグローバル変数ほど悪いとは考えていません。結局のところ、これらの変数へのアクセスはすべて1つのソースファイルに限定されます。その制限により、ファイルスコープ変数はC ++のプライベート静的データメンバーとほぼ同じか、または悪いものであり、それらの使用を禁止しませんか。


1
c ++では、構文の種類により、クラスに多くのメンバー変数を配置することが奨励されるため、プライベート静的メンバーが必要になることはあまりありません。ただし、Cでは、C ++クラスで行うことを完全にエミュレートしない場合は、構造体ポインターをどこにも渡さないようにするために、構造体メンバーの代わりに静的変数またはthread_local変数を使用できます。私の質問はこれについてです。
xiver77 2015

私はstatic、オブジェクトごとではなく、1度だけ存在するストレージを持つメンバーについて話しています。これらの変数は、クラスのコードによって参照されるためにクラスのインスタンスを必要としないため、参照/ポインタをシングルトンに渡す必要はありません。これは、正確にファイルスコープ変数が実現することでもあります。唯一の違いは、「グローバル」変数がファイルスコープを持つのstaticに対し、メンバーはクラススコープをstatic持つことです。しかし、これらの2つのスコープの範囲は非常に似ています。staticただし、のさまざまな意味がわかりにくいことに同意します。
cmaster-モニカを2015年

1
少なくともCでは、staticは実際には変数に対して(関数に対してではなく)1つの意味を持っています。
xiver77 2015

1

それはすべて、私の見解では、変数のスコープ(定数ではなく、変更可能なもの)に関連付けられています。確かに多少のニュアンスが欠けている見方ですが、これは実用的なカウンターであり、「これは絶対に悪いです!」その後、彼らが批判するものに関連するものと同様の問題、たとえば競合状態などにつまずきます。

50,000の行関数があり、あらゆる種類の変数が先頭で宣言されており、gotoステートメントがあちこちに移動しているとします。これはこのような巨大な変数スコープではあまり快適ではなく、関数について推論すること、およびそのような変数で何が行われているのかを判断することは非常に困難になります。そのような巨大なケースでは、「外部」と「内部」の副作用の通常の区別は、その実用的な目的の多くを失います。

80行の単純なプログラムを1回だけ作成し、グローバル変数(内部リンケージとファイルスコープまたは外部リンケージのいずれかを使用して、ただしプログラムが非常に小さい)でクランクアウトするとします。それはそれほど悪くはありません。

実装のための数千行のコードを含むプログラム全体のロジックを含むオブジェクト指向言語の巨大なクラスがあるとします。その場合、そのメンバー変数は、上記の80行のプログラムのグローバル変数よりも問題があります。

コード、そのコードのスレッドセーフティ(またはコードの欠如)をより適切かつ確実に推論できるようにしたい場合は、コードをより予測可能にし、あらゆる種類の潜在的なエッジケースを見逃さずにテストが適切にカバーされるようにします。 、それから変数へのアクセスを狭めるのに役立ちます。

ファイルスコープの静的は、外部リンケージを使用した場合よりもスコープが狭い傾向がありますが、ソースファイルが100,000行のコードである場合でも、かなり幅が広いです。したがって、ファイルスコープの静的については、それらを回避できない場合は、それらにアクセスできるソースファイルを膨大なものにしないようにして、スコープを狭くするようにします。関数(パラメーターを含むローカル変数の場合)、クラス(メンバー変数の場合)、またはモジュール(外部リンケージのあるグローバルの場合はモジュール内でのみアクセス可能)、またはソフトウェア全体(グローバルの場合)ソフトウェア全体にアクセス可能な外部リンケージ)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.