C/C++では、グローバル変数は私の教授が考えているほど悪いのですか?
グローバル変数の問題は、すべての関数がこれらにアクセスできるため、どの関数がこれらの変数を実際に読み書きするかを把握することがますます難しくなることです。
アプリケーションの動作を理解するには、グローバル状態を変更するすべての機能を考慮する必要があります。それは可能ですが、アプリケーションが成長するにつれて、事実上不可能になるまで(または少なくとも完全に時間を浪費するまで)難しくなります。
グローバル変数に依存しない場合、必要に応じて異なる関数間で状態を渡すことができます。そうすれば、グローバルな状態を考慮する必要がないので、各機能が何をするのかを理解するチャンスがはるかに高くなります。
重要なことは、全体的な目標を明確にすることです。
ほとんどの場合、グローバル変数はコードの意味を明確にしないため、「グローバル変数なし」ルールがあります。
ただし、多くのルールと同様に、人々はルールを覚えており、ルールが意図したことを覚えていません。
グローバル変数の悪を避けるために、膨大な数のパラメーターを渡すことでコードのサイズを2倍にするプログラムを見てきました。最終的には、グローバルを使用すると、プログラムを読む人にとってclearerになります。ルールの言葉を無意識に遵守することにより、元のプログラマーはルールの意図に失敗しました。
それで、はい、グローバルはしばしば悪いです。しかし、最終的にグローバル変数を使用することでプログラマの意図が明確になったと感じたら、先に進んでください。ただし、誰かに2番目のコード(グローバル)にアクセスさせて最初の部分がどのように機能するかを理解させると、自動的に生じる明瞭さの低下を覚えておいてください。
教授は次のようなことを言っていました。グローバル変数を正しく使用すれば大丈夫です。それらを正しく使用するのが上手くいったとは思わないので、ほとんど使用しませんでした。
グローバル変数は、代替手段がない場合にのみ使用してください。はい、シングルトンも含まれます。 90%の時間、グローバル変数はパラメーターを渡すコストを節約するために導入されています。そして、マルチスレッド/ユニットテスト/メンテナンスコーディングが発生し、問題が発生します。
そのため、90%の状況でグローバル変数が悪いです。例外はあなたの大学時代には見られないでしょう。私が頭の中で考えられる例外の1つは、割り込みテーブルなどの本質的にグローバルなオブジェクトを扱うことです。 DB接続などseemはグローバルになりますが、そうではありません。
プログラマーに対してグローバル変数が作成する問題は、グローバル変数を使用しているさまざまなコンポーネント間で コンポーネント間結合 サーフェスを拡張することです。これが意味することは、グローバル変数を使用するコンポーネントの数が増えると、相互作用の複雑さが増すことです。このカップリングの増加により、通常、変更を加えるときにシステムに欠陥が入りやすくなり、欠陥の診断と修正が困難になります。また、このカップリングの増加により、変更時に使用可能なオプションの数が減り、変更の結果を判断するためにグローバル変数を使用しているさまざまなモジュールをトレースする必要があるため、変更に必要な労力を増やすことができます。
カプセル化 の目的は、基本的にグローバル変数を使用することの反対であり、ソースをより簡単に、より安全に、より簡単に理解して変更できるように結合を減らすことです。グローバル変数を使用しない場合は、 単体テスト を使用する方がはるかに簡単です。
たとえば、さまざまなコンポーネントが状態マシンとして使用する列挙インジケーターとして使用されている単純なグローバル整数変数があり、新しいコンポーネントに新しい状態を追加して変更を加えた場合、他のすべてをトレースする必要があります変更がそれらに影響しないことを保証するコンポーネント。考えられる問題の例は、現在の値ごとにswitch
ステートメントで列挙型グローバル変数の値をテストするcase
ステートメントがさまざまな場所で使用されている場合です。 switch
ステートメントの一部には、グローバルに関する予期しない値を処理するdefault
ケースがありません。アプリケーションに関する限り、突然、未定義の動作が発生します。
一方、共有データ領域の使用は、アプリケーション全体で参照されるグローバルパラメータのセットを含めるために使用される場合があります。このアプローチは、多くの場合、メモリフットプリントが小さい組み込みアプリケーションで使用されます。
これらの種類のアプリケーションでグローバル変数を使用する場合、通常、データ領域への書き込みの責任は単一のコンポーネントに割り当てられ、他のすべてのコンポーネントはその領域をconst
と見なし、そこから読み取ります。このアプローチを取ることにより、発生する可能性のある問題が制限されます。
回避する必要があるグローバル変数のいくつかの問題
構造体などのグローバル変数のソースが変更された場合、変数を使用するすべてがその真のサイズとメモリテンプレートを認識するように、それを使用するすべてを再コンパイルする必要があります。
複数のコンポーネントがグローバル変数を変更できる場合、グローバル変数に矛盾するデータがあるという問題が発生する可能性があります。マルチスレッドアプリケーションでは、ある種のロックまたはクリティカル領域を追加して、一度に1つのスレッドのみがグローバル変数を変更でき、スレッドが変数を変更しているときにすべての変更が完了するようにする必要があります。他のスレッドが変数を照会または変更する前にコミットされます。
グローバル変数を使用するマルチスレッドアプリケーションのデバッグは、より困難な場合があります。 競合状態 に遭遇すると、複製が困難な欠陥が作成される可能性があります。特にマルチスレッドアプリケーションでは、いくつかのコンポーネントがグローバル変数を介して通信するため、どのコンポーネントが変数をいつどのように変更するかを知ることは、理解するのが非常に困難です。
グローバル変数を使用すると、名前の衝突が問題になる可能性があります。グローバル変数と同じ名前を持つローカル変数は、グローバル変数を隠すことができます。 Cプログラミング言語を使用すると、命名規則の問題も発生します。回避策は、特定のサブシステムのグローバル変数がすべて同じ最初の3文字で始まるサブシステムにシステムを分割することです(これを参照してください Objective Cでの名前空間の衝突の解決 ) 。 C++は名前空間を提供し、Cでは、静的にファイルで提供されるさまざまなデータ項目とデータと関数へのポインタであるグローバルに表示される構造体を作成することでこれを回避できます。グローバルに表示される構造体。
場合によっては、元のアプリケーションの意図が変更され、単一スレッドの状態を提供するグローバル変数が変更されて、複数の重複スレッドが実行できるようになります。例としては、状態にグローバル変数を使用する単一ユーザー向けに設計された単純なアプリケーションがあります。その後、管理からのリクエストが発生し、リモートアプリケーションが仮想ユーザーとして機能できるように RESTインターフェイス を追加します。そのため、グローバル変数とその状態情報を複製する必要が生じたため、単一ユーザーとリモートアプリケーションからの各仮想ユーザーが独自のグローバル変数のセットを持つようになりました。
C++ namespace
およびCのstruct
テクニックの使用
C++プログラミング言語の場合、namespace
ディレクティブは、名前の衝突の可能性を減らすのに非常に役立ちます。 namespace
とclass
およびさまざまなアクセスキーワード(private
、protected
、およびpublic
)は、変数をカプセル化するために必要なほとんどのツールを提供します。ただし、Cプログラミング言語はこのディレクティブを提供していません。このstackoverflowの投稿 Cの名前空間 は、Cにいくつかのテクニックを提供します。
有用な手法は、グローバルな可視性を持つstruct
として定義され、このstruct
内に公開されているさまざまなグローバル変数および関数へのポインターとして定義される単一のメモリ常駐データ領域を持つことです。グローバル変数の実際の定義には、static
キーワードを使用してファイルスコープが与えられます。その後、const
キーワードを使用して、読み取り専用であるかどうかを示すと、コンパイラーは読み取り専用アクセスの実施を支援できます。
struct
テクニックを使用すると、グローバルをカプセル化して、たまたまグローバルであるようなパッケージまたはコンポーネントにすることもできます。この種のコンポーネントを持つことにより、グローバルおよびグローバルを使用する機能に影響を与える変更の管理が容易になります。
ただし、namespace
またはstruct
の手法は名前の衝突の管理に役立ちますが、グローバルの使用が特に現代のマルチスレッドアプリケーションで導入するコンポーネント間カップリングの根本的な問題は依然として存在します。
はい。ただし、グローバル変数を使用するコードでの作業を停止し、グローバル変数を使用するコードを使用する他の何かを書き始めるまで、グローバル変数のコストは発生しません。しかし、コストはまだ残っています。
言い換えれば、それは長期的な間接コストであり、そのため、ほとんどの人はそれは悪くないと考えています。
最高裁判所裁判中にコードが集中レビューで終わる可能性がある場合は、回避するようにしてください。グローバル変数。
この記事を参照してください: バギーブリーザライザーコードはソースレビューの重要性を反映しています
両方の研究で特定されたコードのスタイルにいくつかの問題がありました。レビュアーに関係する文体上の問題の1つは、保護されていないグローバル変数の広範な使用でした。これは、プログラムの状態が不整合になったり、値が誤って変更されたり上書きされたりするリスクが高まるため、貧弱なフォームと見なされます。研究者はまた、コード全体で小数の精度が一貫して維持されていないという事実について懸念を表明しました。
男、私はそれらの開発者がグローバル変数を使用していなかったことを望んでいるに違いない!
グローバル変数は、作成するのと同じくらい悪いです。
完全にカプセル化されたプログラムを作成している場合、グローバルを使用できます。グローバルを使用するのは「罪」ですが、プログラミングの罪は哲学的にややこしいです。
L.in.oleum をチェックアウトすると、変数のみがグローバルな言語が表示されます。ライブラリはすべてグローバルを使用する以外に選択肢がないため、スケーラブルではありません。
とはいえ、選択肢があり、プログラマーの哲学を無視できる場合、グローバルはそれほど悪くありません。
あなたがそれらを正しく使用すれば、どちらもGotosではありません。
大きな「悪い」問題は、それらを間違って使用すると、人々が悲鳴を上げ、火星着陸船がクラッシュし、世界が爆発することです...またはそのようなものです。
この質問に別の質問で答えます: singletons /を使用していますか?シングルトンは悪いですか?
(ほとんどすべての)singeltonの使用は美化されたグローバル変数だからです。
問題は、それらがbadであるということではなく、dangerousであるということです。独自の長所と短所があり、特定のタスクを達成する最も効率的な方法または唯一の方法である状況があります。ただし、常に適切に使用するための措置を講じていても、誤用されやすいveryです。
いくつかの長所:
いくつかの短所:
最初の2つの長所と私がリストした最初の2つの短所は、文言が異なるだけでまったく同じものです。これは、グローバル変数の機能が実際に役立つ可能性があるためですが、それらを便利にする機能そのものがすべての問題の原因です。
問題のいくつかに対するいくつかの潜在的な解決策:
Globals
またはGlobalVars
です)、またはグローバル変数に標準化された命名規則を使用しますglobal_[name]
またはg_module_varNameStyle
として(コメントでunderscore_dで言及されているように)。これにより、その使用法が文書化され(名前空間/構造体名を検索することでグローバル変数を使用するコードを見つけることができます)、グローバル名前空間への影響が最小限に抑えられます。extern
を宣言します。したがって、それらの使用は、それらにアクセスする必要のあるコンパイル単位に制限できます。コードが多くのグローバル変数に依存しているが、各コンパイルユニットが少数のグローバル変数へのアクセスのみを必要とする場合は、それらを複数のソースファイルにソートすることを検討できます。それらが良いか悪いかは、それらをどのように使用するかによります。大多数はそれらをひどく使用する傾向があるため、それらに対する一般的な警戒心です。適切に使用すると、大きな恩恵を受けることができます。ただし、使用方法が不十分な場合は、willが戻ってきて、いつ、どのように期待しないかを噛みます。
それを見る良い方法は、それら自体は悪くないということですが、それらは悪いデザインを可能にし、悪いデザインの効果を指数関数的に増加させることができます。
それらを使用するつもりがない場合でも、それらを安全に使用する方法がわからないため、それらを使用しないよりも、それらを安全に使用する方法を知って、選択しない方が良いです。グローバル変数に依存する既存のコードを維持する必要がある状況に陥った場合、それらを適切に使用する方法がわからないと、困難に直面する可能性があります。
別のスレッドで誰かが言ったように(私は言い換えています)「このようなルールは、そうすることの結果を完全に理解するまで、破られるべきではありません。」
グローバル変数が必要な場合、または少なくとも非常に役立つ場合があります(たとえば、システム定義のコールバックの使用)。一方で、彼らはあなたが言われたすべての理由で非常に危険でもあります。
おそらくエキスパートに任せるべきプログラミングの多くの側面があります。時には非常に鋭いナイフが必要です。しかし、準備が整うまで使用することはできません...
特に、他の人が同じコードで作業していて、変数が参照されるすべての場所を検索するのに20分を費やしたくない場合、グローバル変数は一般的に悪いです。また、変数を変更するスレッドを追加すると、まったく新しいレベルの頭痛の種になります。
単一の翻訳単位で使用される匿名名前空間のグローバル定数は、プロのアプリやライブラリでうまく使用されています。ただし、データが可変である場合、および/または複数のTU間で共有する必要がある場合は、設計のためではなく、コードのデバッグや作業のためにカプセル化することができます。
グローバル変数を使用することは、敷物の下で土を掃除するようなものです。簡単な修正であり、ダストパンや掃除機で掃除するよりも短期的にはずっと簡単です。ただし、後でラグを動かしてしまうと、その下に大きな驚きの混乱が生じます。
グローバル変数は、ローカルでのみ変更する必要があるプログラムの側面を操作できる場合、不適切です。 OOPでは、グローバルは多くの場合、カプセル化のアイデアと競合します。
絶対違う。しかし、それらを誤用しています...それは悪いです。
意図せずにそれらを削除することは、それだけです...心がありません。アドバンテージとデメリットを知らない限り、教えて/学んだように明確に舵を取るのが最善ですが、グローバル変数に暗黙のうちに問題はありません。長所と短所を理解したら、自分で決定してください。
あなたの教授は悪い習慣を始める前に止めようとしていると思います。
グローバル変数には場所があり、多くの人が言ったように、それらをどこで、いつ使用するかを知ることは複雑です。だから私は、教授が禁止することに決めたグローバル変数の理由、方法、時期、および場所の核心に入ろうとは思いません。誰が知っているか、彼は将来それらを禁止するかもしれません。
グローバル変数は小さなプログラムでは問題ありませんが、大きなプログラムで同じように使用すると恐ろしいものになります。
これは、学習しながらそれらを使用する習慣を簡単に身に付けることができることを意味します。これがあなたの教授があなたを保護しようとしているものです。
経験を積むと、大丈夫なときに習得しやすくなります。
はい、無能なプログラマーにそれらを使用させると(特に科学者の90%が読む)、20以上のファイルに広がる600以上のグローバル変数と、80%の関数がvoidを取得して操作する12,000行のプロジェクトになるためです完全にグローバル状態。
プロジェクト全体を知らない限り、ある時点で何が起こっているかを理解することはすぐに不可能になります。
いいえ、まったく悪くありません。この決定を行うには、コンパイラーが生成した(マシン)コードを調べる必要があります。グローバルよりもローカルを使用する方がはるかに悪い場合があります。また、ローカル変数に「静的」を設定することは、基本的にそれをグローバルにすることです(そして、実際のグローバルが解決する他のい問題を作成します)。 「ローカルグローバル」は特に悪いです。
グローバルを使用すると、メモリ使用量も明確に制御できます。これは、ローカルで行うのがはるかに難しいことです。最近は、メモリが非常に限られている組み込み環境でのみ問題になります。組み込みが他の環境と同じであると仮定する前に知っておくべきこと、およびプログラミング規則が全面的に同じであると仮定します。
教えられているルールに疑問を呈するのは良いことです。それらのほとんどはあなたが言われている理由のためではありません。しかし、最も重要な教訓は、これが永遠にあなたと一緒に持ち歩くルールであるということではありませんが、これはこのクラスをパスして前進するために尊重するために必要なルールです。人生では、会社XYZには給料を受け取り続けるために最終的に尊重しなければならない他のプログラミング規則があることに気付くでしょう。どちらの場合でも、ルールを論ずることができますが、学校よりも仕事のほうがはるかに幸運があると思います。あなたは多くの学生のもう一人であり、あなたの席はすぐに交換され、教授は文句を言いません。あなたはこの製品を最後まで見なければならないプレーヤーの小さなチームの1つであり、その環境で開発されたルールはチームメンバーと製品および会社の利益。だから、誰もが気にかけている場合、または特定の製品について、大学で学んだことやジェネリックプログラミングに関する本に違反する正当なエンジニアリング上の理由がある場合は、アイデアをチームは、推奨方法でない場合は有効なものとして書き留めます。現実の世界ではすべてが公正なゲームです。
学校や本で教えられたすべてのプログラミングルールに従うと、プログラミングのキャリアが非常に制限されます。あなたは生き残り、実り多いキャリアをたぶんできますが、利用できる環境の幅と幅は極端に制限されます。ルールがどのように、なぜそこにあり、それを守ることができるかを知っているなら、それは良いことです。理由が「私の先生がそう言ったから」だけなら、それはあまり良くありません。
コンパイラやプロセッサ(および言語)が進化するにつれて、このようなトピックは職場で議論されることが多く、今後もそうであることに注意してください。前進します。
とりあえず、最大の声を出す人や最大のスティックを持っている人が言うことなら何でもします(あなたが最も大きな声を出して最大のスティックを持っている人になるまで)。
私は、このスレッド全体で行われているポイントに反して、マルチスレッドをそれ自体により困難または不可能にしていると主張したいと思います。グローバル変数は共有状態ですが、グローバルの代替手段(たとえば、ポインターを渡す)も状態を共有する場合があります。マルチスレッドの問題は、共有状態を適切に使用する方法であり、その状態がたまたまグローバル変数または他の何かによって共有されているかどうかではありません。
ほとんどの場合、マルチスレッドを実行するときには、何かを共有する必要があります。たとえば、生産者と消費者のパターンでは、作業単位を含むスレッドセーフキューを共有できます。また、そのデータ構造はスレッドセーフであるため、共有することが許可されています。そのキューがグローバルであるかどうかは、スレッドセーフに関してはまったく関係ありません。
グローバルを使用しない場合、プログラムをシングルスレッドからマルチスレッドに変換する方が簡単になるという、このスレッド全体の暗黙の希望。はい。グローバル化により、自分自身を簡単に撃つことができますが、自分自身を撃つ方法はたくさんあります。
私はグローバルを支持していません、他のポイントがまだ立っているので、私のポイントはプログラムのスレッドの数が可変スコープとは何の関係もないということです。
グローバル変数の使用は、実際には要件に依存します。その利点は、値を繰り返し渡すオーバーヘッドを減らすことです。
ただし、教授はセキュリティの問題を発生させるので正しいので、グローバル変数の使用はできる限り避ける必要があります。グローバル変数はまた、時々デバッグするのが難しいである問題を作成します。
例えば:-
変数値がmodified on runtimeになっている状況。その時点では、コードのどの部分がコードを変更しているのか、どの条件であるのかを特定することは困難です。
企業内のWebアプリケーションでは、最適化の理由でセッション/ウィンドウ/スレッド/ユーザー固有のデータをサーバーに保持し、接続が不安定な場合の作業の損失を防ぐために使用できます。前述のように、競合状態を処理する必要があります。この情報にはクラスの単一インスタンスを使用し、慎重に管理されています。
私は通常、シングルトンや動的にロードされるライブラリ内の関数への関数ポインタのようにめったに変更されない値にグローバルを使用します。マルチスレッドアプリケーションで可変グローバルを使用すると、バグの追跡が困難になる傾向があるため、これを一般的なルールとして回避しようとしています。
引数を渡す代わりにグローバルを使用すると、多くの場合より高速になりますが、最近よく行うマルチスレッドアプリケーションを作成している場合、通常はうまく機能しません(スレッドスタティックを使用できますが、パフォーマンスの向上は疑問です) 。
結局のところ、プログラムやアプリは引き続き機能しますが、それは整然としていて、何が起こっているのかを完全に理解しているという問題です。すべての関数間で変数値を共有する場合、どの関数が値を変更しているのかを追跡することが難しくなり(関数がそうする場合)、100万倍のデバッグが難しくなります
Globalは、configurationの場合に適しています。 configuration/changesにglobal impact on プロジェクト全体が必要な場合。
したがって、1つの構成を変更し、changesをプロジェクト全体にリダイレクトできます。しかし、グローバルを使用するには非常に賢明でなければならないことに注意する必要があります。
遅かれ早かれ、その変数の設定方法や、アクセスされたときに何が起こるかを変更する必要があります。または、変更された場所を見つける必要があります。
グローバル変数を持たない方が、実際には常に優れています。 dam getおよびsetメソッドを記述し、1日、1週間、または1か月後にそれらが必要になったときにあなたに感嘆します。
セキュリティとは、変数がグローバルと宣言されている場合、誰でも変数を操作できることを意味します。この例では、銀行プログラムでグローバル変数として残高がある場合、ユーザー関数はこれを操作でき、銀行役員も操作できるように例を挙げて説明しますこれは問題があります。読み取り専用および引き出し機能をユーザーのみに与える必要がありますが、銀行の店員は、ユーザーが個人的に机に現金を渡すときに金額を追加することができます。