C / C ++では、グローバル変数は教授が考えるほど悪いのですか?
C / C ++では、グローバル変数は教授が考えるほど悪いのですか?
回答:
グローバル変数の問題は、すべての関数がこれらにアクセスできるため、これらの変数を実際に読み書きする関数を理解することがますます難しくなることです。
アプリケーションがどのように機能するかを理解するには、グローバル状態を変更するすべての関数を考慮する必要があります。それは可能ですが、アプリケーションが大きくなると、事実上不可能(または少なくとも完全に時間の無駄)になるほど難しくなります。
グローバル変数に依存しない場合、必要に応じて異なる関数間で状態を渡すことができます。このようにすると、グローバルな状態を考慮する必要がないため、各関数の機能を理解できる可能性が高くなります。
重要なことは、全体的な目標を明確にすることです。
ほとんどの場合、グローバル変数によりコードの意味が不明確になるため、「グローバル変数なし」というルールがあります。
ただし、多くのルールと同様に、人々はそのルールを覚えており、そのルールが意図していたことを覚えていません。
グローバル変数の悪を回避するためだけに膨大な数のパラメーターを渡すことにより、コードのサイズを2倍にするプログラムを見たことがあります。結局のところ、グローバルを使用すると、プログラムを読んでいる人にとってプログラムがより明確になります。ルールの言葉を不注意に固執することにより、元のプログラマーはルールの意図に失敗しました。
だから、はい、グローバルはしばしば悪いです。しかし、結局のところ、グローバル変数を使用することでプログラマーの意図がより明確になったと感じたら、先に進んでください。ただし、最初の部分がどのように機能するかを理解するために誰かに2番目のコード(グローバル)に強制的にアクセスさせると自動的に発生する明快さの低下を覚えておいてください。
私の教授は以前に次のようなことを言っていました:グローバル変数の使用は、正しく使用すれば大丈夫です。私は今までそれらを正しく使うのが得意ではなかったと思うので、ほとんど使用しませんでした。
static
グローバル変数を多用しています。言語はCです。比較的小さな翻訳単位に制限されているため、C ++オブジェクトのクラス変数に似ています。
program lifetime, file scope variables
。そして、あなたは(自動変数では不可能である)、外の世界への変数へのポインタを渡したら、彼らは非常にグローバルになる...
static
グローバル変数のスコープは同じ翻訳単位に限定されています。ただし、グローバル変数として、プログラムの終了までの寿命があります。
グローバル変数は、代替手段がない場合にのみ使用してください。そして、はい、それはシングルトンを含みます。90%の確率で、パラメーターを渡すコストを節約するためにグローバル変数が導入されます。そして、マルチスレッド/ユニットテスト/メンテナンスコーディングが発生し、問題が発生します。
したがって、はい、状況の90%でグローバル変数が不良です。例外はあなたの大学時代にあなたに見られることはほとんどありません。私が頭の中で考えている1つの例外は、割り込みテーブルなどの本質的にグローバルなオブジェクトを扱うことです。DB接続のようなものはグローバルに見えますが、そうではありません。
プログラマーのためにグローバル変数が作成する問題は、グローバル変数を使用しているさまざまなコンポーネント間のコンポーネント間結合面を拡張することです。これが意味することは、グローバル変数を使用するコンポーネントの数が増えると、相互作用の複雑さも増す可能性があるということです。この増加した結合により、通常、変更を加えるときに欠陥がシステムに注入されやすくなり、欠陥の診断と修正が難しくなります。この結合の増加により、変更を行うときに使用可能なオプションの数が減り、変更の結果を判別するためにグローバル変数も使用しているさまざまなモジュールをたどる必要があるため、変更に必要な労力を増やすことができます。
カプセル化の目的は、基本的にグローバル変数の使用とは逆ですが、結合を減らして、ソースの理解と変更をより簡単に、より安全に、より簡単にテストできるようにすることです。グローバル変数を使用しない場合は、単体テストを使用する方がはるかに簡単です。
たとえば、さまざまなコンポーネントがステートマシンとして使用する列挙型インジケーターとして使用されている単純なグローバル整数変数があり、新しいコンポーネントに新しい状態を追加して変更を加えた場合、他のすべてをトレースする必要があります。変更がコンポーネントに影響を及ぼさないようにするためのコンポーネント。考えられる問題の例としては、現在の各値のステートメントを含むswitch
列挙型グローバル変数の値をテストするステートメントcase
がさまざまな場所で使用されており、一部のswitch
ステートメントでdefault
処理するケースがない場合があります。アプリケーションに関する限り、突然予期しないグローバルな動作が発生します。
一方、共有データ領域を使用すると、アプリケーション全体で参照される一連のグローバルパラメータを含めることができます。このアプローチは、メモリフットプリントが小さい組み込みアプリケーションでよく使用されます。
これらの種類のアプリケーションでグローバル変数を使用する場合、通常、データ領域への書き込みの責任は単一のコンポーネントに割り当てられ、他のすべてのコンポーネントはその領域をその領域としてconst
認識し、そこから読み取り、書き込みは行いません。このアプローチを取ると、発生する可能性のある問題が制限されます。
回避する必要があるグローバル変数からのいくつかの問題
構造体などのグローバル変数のソースが変更された場合、その変数を使用するすべてがその実際のサイズとメモリテンプレートを認識できるように、それを使用するすべてを再コンパイルする必要があります。
複数のコンポーネントがグローバル変数を変更できる場合は、グローバル変数にあるデータに一貫性がないという問題が発生する可能性があります。マルチスレッドアプリケーションでは、おそらく何らかのロック領域またはクリティカル領域を追加して、一度に1つのスレッドのみがグローバル変数を変更できるようにし、スレッドが変数を変更しているときにすべての変更が完了するようにする必要があります。他のスレッドが変数を照会または変更する前にコミットされます。
グローバル変数を使用するマルチスレッドアプリケーションのデバッグは、より困難になる可能性があります。あなたはに実行することができます競合状態で複製することが困難な欠陥を作成することができます。複数のコンポーネントがグローバル変数を介して通信するため、特にマルチスレッドアプリケーションでは、どのコンポーネントが変数を変更しているのか、いつどのように変数を変更しているのかを理解するのは非常に困難です。
名前の衝突は、グローバル変数の使用に関する問題になる可能性があります。グローバル変数と同じ名前のローカル変数は、グローバル変数を非表示にすることができます。また、Cプログラミング言語を使用すると、命名規則の問題が発生します。回避策は、特定のサブシステムのグローバル変数をすべて同じ最初の3文字で始めるサブシステムにシステムを分割することです(目的Cでの名前空間の競合の解決については、こちらを参照してください)。C ++は名前空間を提供しますが、Cを使用すると、さまざまなデータ項目と、ファイルとして静的に提供されるデータおよび関数へのポインターをメンバーとするグローバルに表示される構造体を作成することでこれを回避できます。グローバルに見える構造体。
場合によっては、元のアプリケーションの意図が変更され、単一のスレッドの状態を提供するグローバル変数が変更されて、複数の重複するスレッドを実行できるようになります。例としては、状態のグローバル変数を使用する単一ユーザー向けに設計されたシンプルなアプリケーションがあり、リモートアプリケーションが仮想ユーザーとして機能できるようにRESTインターフェースを追加する要求が管理から降りてきます。したがって、グローバル変数とその状態情報を複製して、単一のユーザーとリモートアプリケーションの各仮想ユーザーが独自の一意のグローバル変数のセットを持つようにする必要があります。
C ++ namespace
とC のstruct
テクニックの使用
C ++プログラミング言語の場合、namespace
ディレクティブは名前の衝突の可能性を減らすのに非常に役立ちます。namespace
とともにclass
、さまざまなアクセスキーワード(、、)private
はprotected
、public
変数をカプセル化するために必要なほとんどのツールを提供します。ただし、Cプログラミング言語はこのディレクティブを提供しません。このスタックオーバーフローの投稿であるNamespaces in Cは、Cにいくつかのテクニックを提供します。
有用な手法は、struct
グローバルな可視性を持つとして定義されている単一のメモリ常駐データ領域を持ちstruct
、その中に、公開されているさまざまなグローバル変数と関数へのポインターがあります。グローバル変数の実際の定義には、static
キーワードを使用してファイルスコープが与えられます。次に、const
キーワードを使用して、読み取り専用を示す場合、コンパイラーは読み取り専用アクセスを強制するのに役立ちます。
このstruct
手法を使用すると、グローバルをカプセル化して、たまたまグローバルになる一種のパッケージまたはコンポーネントにすることもできます。この種のコンポーネントを使用することで、グローバルとグローバルを使用する機能に影響を与える変更の管理が容易になります。
ただし、namespace
このstruct
手法は名前の衝突を管理するのに役立ちますが、グローバルの使用が特に最近のマルチスレッドアプリケーションで導入するコンポーネント間結合の根本的な問題は依然として存在しています。
グローバル変数は、あなたが作ったのと同じくらい悪いものです。
完全にカプセル化されたプログラムを作成する場合は、グローバルを使用できます。グローバルを使用することは「罪」ですが、プログラミングの罪はかなり哲学的です。
L.in.oleumをチェックアウトすると、変数がグローバルのみである言語が表示されます。ライブラリはすべてグローバルを使用せざるを得ないため、スケーラブルではありません。
とはいえ、選択肢があり、プログラマーの哲学を無視できる場合、グローバルはそれほど悪くありません。
あなたがそれらを正しく使うならば、どちらも後藤ではありません。
大きな「悪い」問題は、それらを間違って使用すると、人々が悲鳴を上げ、火星着陸船が墜落し、世界が爆破されるということです...またはそのようなもの。
最高裁判所の裁判中にコードが徹底的に審査される可能性がある場合は、グローバル変数を回避するようにしてください。
この記事を参照してください: バギー呼吸器コードはソースレビューの重要性を反映しています
両方の調査で特定されたコードのスタイルにいくつかの問題がありました。レビューアが懸念した文体上の問題の1つは、保護されていないグローバル変数の広範な使用でした。プログラムの状態が不整合になったり、値が誤って変更または上書きされたりするリスクが高まるため、これは不適切な形式と見なされます。研究者たちはまた、小数精度がコード全体で一貫して維持されていないという事実についての懸念を表明しました。
男、私はそれらの開発者がグローバル変数を使用していないことを望んでいるに違いない!
私はこの質問に別の質問で答えます:singeltonsを使用していますか/ singeltonsは悪いですか?
(ほとんどすべての)singeltonの使用法は栄光のグローバル変数だからです。
問題は、それらが悪いことではなく、危険なことです。彼らには独自の長所と短所があり、特定のタスクを達成するための最も効率的な方法または唯一の方法である場合があります。ただし、常に適切に使用するための手順を踏んでも、誤用が非常に簡単です。
いくつかの長所:
いくつかの短所:
もしそうなら、私がリストした最初の2つの長所と最初の2つの短所はまったく異なるものですが、言葉遣いが異なります。これは、グローバル変数の機能が実際に役立つ可能性があるためです。しかし、それらを有用にする機能そのものが、すべての問題の原因となっています。
一部の問題に対するいくつかの潜在的な解決策:
Globals
またはの良い例がGlobalVars
)で配置するか、グローバル変数に標準化された命名規則を使用します(global_[name]
またはなどg_module_varNameStyle
(コメントのunderscore_dで言及) ))。これにより、その使用法が文書化され(名前空間/構造体の名前を検索してグローバル変数を使用するコードを見つけることができます)、グローバル名前空間への影響を最小限に抑えることができます。extern
、関連するヘッダーで宣言することで、それらの使用を、それらにアクセスする必要があるコンパイルユニットに限定することができます。コードが多くのグローバル変数に依存しているが、各コンパイルユニットはそれらのほんの一部にアクセスするだけでよい場合は、それらを複数のソースファイルにソートすることを検討できます。そのため、各ファイルのグローバル変数へのアクセスを制限する方が簡単です。それらが良いか悪いかはあなたがそれらをどのように使うかにかかっています。大多数はそれらを悪用する傾向があり、それゆえそれらに対する一般的な警戒心です。適切に使用すれば、それらは大きな恩恵になる可能性があります。悪い使用している場合、しかし、彼らがすることができますあなたを噛まないように戻ってきたとき、あなたは少なくともそれを期待しますか。
それを見る良い方法は、それら自体は悪くないが、悪いデザインを可能にし、悪いデザインの影響を指数関数的に増加させることができるということです。
それらを使用するつもりがない場合でも、安全に使用する方法がわからないため、それらを使用しないよりも、安全に使用する方法を理解し、使用しないことをお勧めします。グローバル変数に依存する既存のコードを維持する必要がある状況に遭遇した場合、それらを適切に使用する方法を知らないと、困難に陥る可能性があります。
g_module_varNameStyle
完全に読みやすいと思います。明確にするために、グローバルを簡単に回避できる場合はキーワードを簡単に使用します。キーワードは簡単です。なぜなら、回避する必要がある、または難読化する必要があるとは信じられなくなったためです。コードは(衝撃!)はるかに整然としています
別のスレッドで誰かが言ったように(言い換えれば)、「このようなルールは、そうすることの結果を完全に理解するまで破られるべきではありません。」
グローバル変数が必要な場合、または少なくとも非常に役立つ場合があります(たとえば、システム定義のコールバックでの作業)。一方、あなたが言われたすべての理由のためにそれらはまた非常に危険です。
おそらくエキスパートに任せるべきプログラミングの多くの側面があります。時々あなたは非常に鋭いナイフが必要です。しかし、準備が整うまでは使用できません...
グローバル変数の使用は、じゅうたんの下の汚れを掃除するようなものです。それは迅速な修正であり、短期的には、ダストパンや掃除機で掃除するよりもはるかに簡単です。ただし、後で敷物を移動してしまうと、その下に大きな驚きが生じます。
絶対違う。しかし、それらを誤用する...それは悪いです。
ために不注意にそれらを削除するのはそれだけです... 長所と短所を知らない限り、教えられた/学んだように、明確に舵を切り、行動するのが最善ですが、グローバル変数に暗黙の問題はありません。あなたが長所と短所を理解するときは、あなた自身の決断をする方が良いです。
いいえ、まったく悪くはありません。この決定を行うには、コンパイラによって生成された(マシン)コードを調べる必要があります。グローバルよりローカルを使用する方がはるかに悪い場合があります。また、ローカル変数に「静的」を置くと、基本的にグローバルになります(そして、実際のグローバルが解決する他の醜い問題が発生します)。「ローカルグローバル」は特に悪い。
グローバルを使用すると、メモリ使用量をクリーンに制御できます。これは、ローカルで行うのがはるかに難しいことです。最近では、メモリが非常に限られている組み込み環境でのみ問題になります。組み込みが他の環境と同じであり、プログラミングルールが全体で同じであると想定する前に知っておくべきこと。
あなたが教えられているルールに疑問を呈するのは良いことです、それらのほとんどはあなたが言われている理由のためではありません。ただし、最も重要な教訓は、これは永遠に持ち運ぶためのルールであるということではありませんが、このクラスに合格して前進するために遵守する必要があるルールです。実生活では、XYZ社の場合、給料を受け取り続けるために最終的に尊重しなければならない他のプログラミングルールがあることがわかります。どちらの状況でもルールを議論することができますが、私はあなたが学校よりも仕事ではるかに幸運になると思います。あなたは多くの学生のほんの1人であり、あなたの席はすぐに交換されます、教授はしません、あなたはこの製品を最後まで見る必要があるプレーヤーの小さなチームの1人であり、その環境で開発されたルールはチームメンバーだけでなく、製品と会社の利益 誰もが気にされている場合、または特定の製品について、大学で学んだことや一般的なプログラミングに関する本に違反するエンジニアリング上の理由がある場合は、チームにそのアイデアを売り、有効な方法として書き留めてください。 。すべてが現実の世界では公正なゲームです。
学校や本で教えられているすべてのプログラミング規則に従えば、プログラミングのキャリアは非常に制限されます。あなたはおそらく生き残り、実りあるキャリアを持つことができますが、あなたが利用できる環境の幅と幅は非常に制限されます。ルールが存在する理由と理由を理解し、それを守ることができるのであれば、それは良いことです。もしあなたの理由が「私の先生がそう言ったから」であるなら、それはあまり良くありません。
このようなトピックは職場で議論されることが多く、コンパイラとプロセッサ(および言語)が進化するにつれ、今後もそうなるでしょう。そのため、この種のルールを守り、自分の立場を守ったり、おそらく他の意見を持つ誰かからレッスンを受けたりしないでください。前進する。
それまでの間、最も大きな声で話すか、最大のスティックを運ぶ人が言うことをすべて実行します(あなたが最も大きな声で叫び、最大のスティックを運ぶような時まで)。
このスレッド全体でマルチスレッド化自体が困難または不可能になるという点に反対したいと思います。グローバル変数は状態を共有しますが、グローバルの代替手段(ポインターの受け渡しなど)も状態を共有する場合があります。マルチスレッドの問題は、共有状態がグローバル変数などで共有されているかどうかではなく、共有状態を適切に使用する方法です。
ほとんどの場合、マルチスレッドを実行するときには、何かを共有する必要があります。たとえば、プロデューサー/コンシューマーパターンでは、作業単位を含むスレッドセーフキューを共有する場合があります。そして、そのデータ構造はスレッドセーフなので、共有することができます。そのキューがグローバルであるかどうかは、スレッドセーフに関してはまったく関係ありません。
グローバルを使用しない場合は、プログラムをシングルスレッドからマルチスレッドに変換する方が簡単であるという、このスレッド全体で示されている暗黙の希望。はい、グローバルを使用すると、足元で自分を簡単に撃つことができますが、自分を撃つ方法はたくさんあります。
他の点がまだ残っているので、私はグローバルを主張していません。私のポイントは、プログラム内のスレッドの数は変数のスコープとは何の関係もないということです。
セキュリティとは、変数がglobalと宣言されている場合、誰でも変数を操作できることを意味します。これを説明するために、銀行プログラムにグローバル変数としてバランスがある場合、ユーザー関数がこれを操作できるようになり、銀行の役員も操作できるようになります。これは問題があります。唯一のユーザーに読み取り専用と引き出し機能を与える必要がありますが、銀行の店員はユーザーが個人的に机に現金を与えるときに金額を追加できます。これはそれが機能する方法です
マルチスレッドアプリケーションでは、グローバル変数の代わりにローカル変数を使用して、競合状態を回避します。
複数のスレッドが共有リソースにアクセスし、少なくとも1つのスレッドがデータへの書き込みアクセス権を持つ場合に、競合状態が発生します。その場合、プログラムの結果は予測できず、さまざまなスレッドによるデータへのアクセス順序に依存します。