ループ外で変数を宣言することとループ内で静的に宣言することの違いは何ですか?


9

これらは、ループ(または任意の関数)の外で変数を保持できる2つの方法です。

最初に、ループの外側のグローバルスコープで宣言できます。

void setup()
{
    Serial.begin(9600);
}

int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    delay(250);
}

ループ内で静的に宣言することもできます。

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

これにより、どのような違いがありますか?

回答:


10

最も基本的な違いはスコープです。

最初のケースでは、グローバル変数を宣言しています。定義後にすべてのスコープでアクセスできる変数です。

void setup()
{
    Serial.begin(9600);
}

void inc();
int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

void inc() //Can edit the value of count
{
  count=count+1;
};

2番目のケースでは、ローカルスコープで静的変数を宣言しています。変数は、グローバル変数と同様にプログラム全体で実行されますが、宣言されているコードブロックでのみアクセスできます。これは同じ例ですが、変更が1つだけあります。countは内で静的変数として宣言されていますloop

void inc();

void loop()
{
    static int count = 0;
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

関数inc()はにアクセスできないため、これはコンパイルされませんcount

グローバル変数は、一見便利なように見えますが、いくつかの落とし穴があります。これらは、物理的な環境と相互作用する可能性のあるプログラムを書くことになると、損傷を引き起こす可能性さえあります。これは、プログラムが大きくなるとすぐに発生する可能性が非常に高い基本的な例です。関数が誤ってグローバル変数の状態を変更する場合があります。

void setup()
{
    Serial.begin(9600);
}
void another_function();
int state=0;

void loop()
{
    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Inadvertently changes state
  state=1;

}

このようなケースはデバッグが非常に困難です。ただし、このタイプの問題は、静的変数を使用するだけで簡単に検出できます。

void setup()
{
    Serial.begin(9600);
}
void another_function();

void loop()
{
    static int state=0;

    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Results in a compile time error. Saves time.
  state=1;

}

5

機能の観点から見ると、どちらの場合もの値はcountの実行の間に保存されるため、両方のバージョンが同じ結果を生成しますloop()(グローバル変数であるか、グローバル変数としてマークされstaticているため、その値を保持するため)。

したがって、どちらを選択するかの決定は、次の議論に帰着します。

  1. 一般に、コンピュータサイエンスでは、スコープの観点から変数をできるだけローカルに保つことをお勧めします。これにより、通常、コードがより明確になり、副作用が少なくなり、他の誰かがそのグローバル変数を使用してロジックを混乱させる可能性が低くなります)。たとえば、最初の例では、他のロジック領域がcount値を変更する可能性がありますが、2番目の例では、その特定の関数のみが変更loop()できます)。
  2. グローバル変数と静的変数は常にメモリを占有しますが、ローカル変数はスコープ内にあるときにのみ実行します。上記の例では違いはありませんが(1つはグローバルを使用し、もう1つは静的変数を使用するため)、大きくて複雑なプログラムでは、静的でないローカルを使用してメモリを節約できます。 ただし、非常に頻繁に実行されるロジック領域に変数がある場合は、静的またはグローバルにすることを検討してください。そうしないと、そのロジック領域に入るたびにわずかなパフォーマンスが失われます。その新しい変数インスタンスにメモリを割り当てます。メモリ負荷とパフォーマンスのバランスを見つける必要があります。
  3. 静的分析のためのより良いレイアウトやコンパイラーによる最適化などの他のポイントも関係するかもしれません。
  4. いくつかの特別なシナリオでは、静的要素の予測不可能な初期化順序に問題がある可能性があります(その点については不明ですが、このリンクを比較してください)。

ソース:arduino.ccの同様のスレッド


Arduinoでは同時実行性がサポートされていないため、再入可能性が問題になることはありません。
Peter Bloomfield

そうだね。それはより一般的なポイントでしたが、確かにArduinoには関係ありません。そのビットを削除しました。
フィリップアルガイエ2014年

1
スコープ内で宣言された静的変数は常に存在し、グローバル変数と同じスペースを使用します!OPコードでは、唯一の違いは、どのコードが変数にアクセスできるかです。scipe staticでは、同じスコープ内でアクセスできます。
jfpoilpret 2014年

1
@jfpoilpretもちろんそれは真実であり、私の回答のそれぞれの部分は少し誤解を招くようでした。修正しました。
フィリップ

2

どちらの変数も静的です-実行セッション全体を通して持続します。グローバルが宣言されていて定義されていない場合、または関数が同じコンパイル単位(ファイル+インクルード)の定義に従っている場合、グローバルはどの関数からも見えます。

の定義をcount関数の内部に移動すると、可視性の範囲が最も近い囲まれた{}esのセットに制限され、関数の呼び出しの有効期間が与えられます(関数の開始と終了時に作成および破棄されます)。これを宣言するとstatic、実行セッションの開始から終了までの間に存在する実行セッションの存続期間が与えられ、関数呼び出し全体で存続します。

ところで、関数内で初期化されたstaticを使用する場合は注意が必要です。これは、一部のバージョンのgnuコンパイラーがこれを誤るのを見たからです。初期化子を持つ自動変数は、すべての関数エントリで作成および初期化する必要があります。初期化子を持つstaticは、実行セットアップ中にmain()に制御が与えられる前に(グローバルと同じように)1回だけ初期化する必要があります。自動であるかのように、各関数エントリでローカルスタティックを再初期化しましたが、これは誤りです。独自のコンパイラをテストして確認します。


グローバルを宣言する関数についてあなたが何を言っているのか理解できません。って意味externですか?
Peter Bloomfield

@ PeterR.Bloomfield:私の投稿のどの部分について質問しているかわかりませんが、私はOPの2つの例を参照していました。1つは本質的にグローバルな定義で、もう1つはローカルの静的です。
JRobert

-3

Atmelのドキュメントによると、「グローバル変数が宣言されている場合、SRAM内の一意のアドレスがプログラムのリンク時にこの変数に割り当てられます。」

完全なドキュメントはこちらです(グローバル変数のヒント#2):http ://www.atmel.com/images/doc8453.pdf


4
どちらの例もSRAMで一意のアドレスになってしまうのではないですか?彼らは両方とも持続する必要があります。
Cyber​​gibbons 2014年

2
はい、実際には、同じドキュメントのヒント#6でその情報を見つけることができます
jfpoilpret
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.