60

どうやら、「絶対にカプセル化しないで!(単なるマクロでも!)」から「大したことないから便利な時に使ってね」など、さまざまな意見があるようです。

そう。

具体的な理由(できれば例を挙げて)

  • グローバル変数が危険な理由
  • 代替変数の代わりにグローバル変数を使用する必要がある場合
  • グローバル変数を不適切に使用する誘惑に駆られている人のために、どのような代替手段がありますか

これは主観的なものですが、私は 1 つの回答を選び (私にとっては、すべての開発者がグローバルに対して持つべき愛憎関係を最もよく表しているものです)、コミュニティはその回答をすぐ下に投票します。

初心者がこの種の参照を持つことは重要だと思いますが、あなたのものと実質的に同様の別の回答が存在する場合は、それを混乱させないでください-コメントを追加するか、他の人の回答を編集してください.

-アダム

4

16 に答える 16

57

変数のスコープは常に可能な限り小さくする必要があります。その背後にある議論は、スコープを拡大するたびに、変数を変更する可能性のあるコードが増えるため、ソリューションがより複雑になるということです。

したがって、設計と実装で自然に許可されている場合は、グローバル変数の使用を避けることが望ましいことは明らかです。このため、本当に必要でない限り、グローバル変数を使用しないことを好みます。

「決して」という声明にも同意できません。他の概念と同様に、グローバル変数は必要な場合にのみ使用する必要があります。真の意図を覆い隠すだけの人工的な構造 (ポインターを渡すなど) を使用するよりも、むしろグローバル変数を使用したいと考えています。

グローバル変数が使用されるいくつかの良い例は、組み込みシステムでのシングルトン パターンの実装またはレジスタ アクセスです。

グローバル変数の過剰な使用を実際に検出する方法について: インスペクション、インスペクション、インスペクション。グローバル変数を目にするたびに、自問自答する必要があります。それはグローバル スコープで本当に必要なのか?

于 2008-10-06T20:58:01.390 に答える
20

グローバル変数を機能させる唯一の方法は、一意であることを保証する名前を付けることです。

その名前には通常、いくつかの「モジュール」または関数のコレクションに関連付けられたプレフィックスがあり、グローバル変数が特に焦点を当てているか意味があります。

これは、変数がそれらの関数に「属している」ことを意味します-それはそれらの一部です。実際、グローバルは通常、他の関数と一緒に実行される小さな関数で「ラップ」できます-同じ.hファイルの同じ名前のプレフィックスで。

ボーナス。

それをすると、突然、それは本当にグローバルではなくなります。これは、関連する機能の一部のモジュールの一部になりました。

これはいつでも実行できます。少し考えれば、以前のグローバル変数はすべて関数のコレクションに割り当てられ、特定の.hファイルに割り当てられ、何も壊さずに変数を変更できる関数で分離できます。

「グローバル変数を絶​​対に使用しないでください」と言うのではなく、「グローバル変数の責任を、最も意味のあるモジュールに割り当ててください」と言うことができます。

于 2008-10-06T22:15:37.040 に答える
11

この公案を考えてみてください。「範囲が十分に狭い場合、すべてがグローバルです」。

この時代でも、1回限りの作業を行うために非常に迅速なユーティリティプログラムを作成する必要がある可能性は非常に高いです。

このような場合、変数への安全なアクセスを作成するために必要なエネルギーは、このような小さなユーティリティで問題をデバッグすることによって節約されるエネルギーよりも大きくなります。

これは、グローバル変数が賢明であり、比較的まれであると私が考えることができる唯一のケースです。脳の短期記憶内に完全に保持できるほど小さい有用で斬新なプログラムはますますまれになっていますが、それでも存在しています。

実際、プログラムがこれほど小さくなければ、グローバル変数は違法であるはずだと大胆に主張することができます。

  • 変数が決して変更されない場合、それは定数であり、変数ではありません。
  • 変数にユニバーサルアクセスが必要な場合は、変数を取得および設定するための2つのサブルーチンが存在し、それらを同期する必要があります。
  • プログラムが小さく開始し、後で大きくなる可能性がある場合は、プログラムが今日大きいかのようにコーディングし、グローバル変数を廃止します。すべてのプログラムが成長するわけではありません!(もちろん、それはプログラマーが時々コードを捨てることをいとわないことを前提としています。)
于 2008-10-06T21:35:40.193 に答える
10

C のグローバル変数は、(変数を各メソッドに渡すのではなく) 複数のメソッドで変数が必要な場合に、コードを読みやすくするのに役立ちます。ただし、すべての場所でその変数を変更できるため、バグの追跡が困難になる可能性があるため、危険です。グローバル変数を使用する必要がある場合は、常に 1 つのメソッドによってのみ直接変更され、他のすべての呼び出し元がそのメソッドを使用するようにしてください。これにより、その変数の変更に関連する問題のデバッグがはるかに簡単になります。

于 2008-10-06T20:44:45.427 に答える
9

スレッドセーフなコードについて心配していない場合は、意味のある場所、つまり、何かをグローバル状態として表現するのが意味のある場所で使用してください。

コードがマルチスレッドである可能性がある場合:絶対に避けてください。グローバル変数をワークキューまたはその他のスレッドセーフ構造に抽象化するか、どうしても必要な場合は、プログラムのボトルネックになる可能性があることに注意して、それらをロックでラップします。

于 2008-10-06T21:29:39.737 に答える
4

防衛産業で働き始めるまで、私は「ネバー」陣営から来ました。ソフトウェアが動的 (C の場合は malloc) メモリの代わりにグローバル変数を使用することを要求する業界標準がいくつかあります。私が取り組んでいるいくつかのプロジェクトでは、動的メモリ割り当てへのアプローチを再考する必要があります。適切なセマフォやスレッドなどで「グローバル」メモリを保護できる場合、これはメモリ管理に受け入れられるアプローチになります。

于 2008-10-06T20:56:28.430 に答える
4

考慮すべき最適化は、コードの複雑さだけではありません。多くのアプリケーションでは、パフォーマンスの最適化がはるかに優先されます。しかし、より重要なことは、グローバル変数を使用すると、多くの状況でコードの複雑さを大幅に軽減できることです。グローバル変数が受け入れられるソリューションであるだけでなく、推奨される多くの、おそらく特殊な状況があります。私のお気に入りの特殊な例は、アプリケーションのメイン スレッドと、リアルタイム スレッドで実行されるオーディオ コールバック関数との間の通信を提供するためにそれらを使用することです。

複数のスレッドで変更にさらされている場合、スコープに関係なく、ANY 変数が潜在的に問題になるため、グローバル変数がマルチスレッド アプリケーションで問題になると示唆するのは誤解を招きます。

グローバル変数は慎重に使用してください。グローバル名前空間の使用を整理および分離するために、可能な限りデータ構造を使用する必要があります。

可変スコープは、プログラマにとって非常に便利な保護に役立ちますが、コストがかかる可能性があります。私は経験豊富な Objective-C プログラマーであり、データ アクセスにおけるオブジェクト指向の障壁にしばしば不満を感じているため、今夜グローバル変数について書くようになりました。アンチグローバルの熱狂は、主にオブジェクト指向 API を単独で使用した経験があり、システム レベルの API とアプリケーション開発におけるそれらの相互作用の深い実践的な経験を持たない、理論に精通した若いプログラマーから主にもたらされていると私は主張します。しかし、ベンダーが名前空間をずさんに使用するとイライラすることは認めざるを得ません。たとえば、いくつかの Linux ディストリビューションでは、"PI" と "TWOPI" がグローバルに事前定義されていたため、私の個人的なコードの多くが壊れていました。

于 2010-12-03T08:55:44.497 に答える
2

グローバル変数がどのようなコンテキストで使用されるかも考慮する必要があります。将来、このコードを複製する必要がありますか。

たとえば、システム内でソケットを使用してリソースにアクセスしている場合です。将来、これらのリソースの複数にアクセスする必要がありますか?答えが「はい」の場合、そもそもグローバルに近づかないので、主要なリファクタリングは必要ありません。

于 2008-10-06T20:55:51.787 に答える
1

定数を宣言するとき。

于 2008-10-06T21:20:12.487 に答える
1

いくつかの理由が考えられます:

デバッグ/テスト目的 (警告 - このコードはテストしていません):

#include <stdio.h>
#define MAX_INPUT 46
int runs=0;
int fib1(int n){
    ++runs;
    return n>2?fib1(n-1)+fib1(n-2):1;
};
int fib2(int n,int *cache,int *len){
    ++runs;
    if(n<=2){
        if(*len==2)
            return 1;
        *len=2;
        return cache[0]=cache[1]=1;
    }else if(*len>=n)
        return cache[n-1];
    else{
        if(*len!=n-1)
            fib2(n-1,cache,len);
        *len=n;
        return cache[n-1]=cache[n-2]+cache[n-3];
    };
};
int main(){
    int n;
    int cache[MAX_INPUT];
    int len=0;
    scanf("%i",&n);
    if(!n||n>MAX_INPUT)
        return 0;
    printf("fib1(%i)==%i",n,fib1(n));
    printf(", %i run(s)\n",runs);
    runs=0;
    printf("fib2(%i)==%i",n,fib2(n,&cache,&len));
    printf(", %i run(s)\n",runs);
    main();
};

私は fib2 にスコープ変数を使用しましたが、それはグローバルが役立つ可能性があるもう 1 つのシナリオです (データを永久に取得することを避けるためにデータを保存する必要がある純粋な数学関数)。

一度だけ使用するプログラム (コンテストなど)、または開発時間を短縮する必要がある場合

グローバルは、どこかの関数が int ではなく *int を必要とする型付き定数として役立ちます。

プログラムを 1 日以上使用する場合は、通常、グローバルを避けます。

于 2011-02-19T17:28:20.363 に答える
1

これは、通常使い回されている他のツールと同じですが、悪いものではないと思います。

たとえば、実際にオンライン データベースのように機能するプログラムがあります。データはメモリに保存されますが、他のプログラムで操作できます。データベース内のストアド プロシージャやトリガーのように動作する内部ルーチンがあります。

このプログラムには何百ものグローバル変数がありますが、考えてみると、データベースとは何なのか、膨大な数のグローバル変数とは何でしょう。

このプログラムは、多くのバージョンを通じて約 10 年間使用されてきましたが、一度も問題が発生したことはありません。

この場合、グローバル変数は、オブジェクトの状態を変更するために使用されるメソッドを持つオブジェクトであることを認めます。オブジェクトの状態を変更するルーチンにいつでもブレーク ポイントを設定できるので、デバッグ中にオブジェクトを変更したユーザーを追跡することは問題ではありません。または、さらに単純に、変更をログに記録する組み込みのログをオンにするだけです。

于 2008-10-06T20:50:24.933 に答える
1

複数の関数がデータにアクセスしたり、オブジェクトに書き込む必要がある場合は、グローバル変数を使用する必要があります。たとえば、単一のログ ファイル、接続プール、またはアプリケーション全体でアクセスする必要があるハードウェア参照など、複数の関数にデータまたは参照を渡す必要がある場合などです。これにより、非常に長い関数宣言と重複データの大規模な割り当てが回避されます。

グローバル変数は、明示的に指示された場合、またはプログラムが終了した場合にのみクリーンアップされるため、絶対に必要な場合を除き、通常はグローバル変数を使用しないでくださいマルチスレッド アプリケーションを実行している場合、複数の関数が変数に同時に書き込むことができます。バグがある場合、変数を変更している関数がわからないため、そのバグを追跡するのがより困難になる可能性があります。グローバル変数に一意の名前を明示的に付ける命名規則を使用しない限り、名前の競合の問題にも遭遇します。

于 2008-10-06T21:05:09.753 に答える
0

グローバル定数は便利です。プリプロセッサ マクロよりも型の安全性が高く、必要に応じて値を簡単に変更できます。

グローバル変数にはいくつかの用途があります。たとえば、プログラムの多くの部分の操作がステート マシンの特定の状態に依存している場合などです。変数を変更できる場所の数を制限している限り、変数に関連するバグを追跡することはそれほど悪くありません。

グローバル変数は、複数のスレッドを作成するとすぐに危険になります。その場合、スコープを(せいぜい)ファイルグローバル(静的に宣言することにより)変数と、危険な可能性のある複数のアクセスから保護するゲッター/セッターメソッドに制限する必要があります。

于 2009-01-21T02:23:54.727 に答える
-1

私はここで「決して」キャンプにいません。グローバル変数が必要な場合は、少なくともシングルトン パターンを使用してください。そうすれば、遅延インスタンス化の利点を享受でき、グローバル名前空間が乱雑になることはありません。

于 2008-10-06T20:49:18.023 に答える