28

ファイルスコープで静的グローバル変数と静的揮発変数を使用しましたが、

どちらも ISR とメイン ループによって更新され、メイン ループは変数の値をチェックします。

ここでは、最適化中にグローバル変数も揮発性変数も最適化されません。したがって、揮発性変数を使用する代わりに、グローバル変数が問題を解決します。

では、揮発性の代わりにグローバル変数を使用するのは良いことですか?

static volatile を使用する特定の理由??

どんなサンプルプログラムも評価できるでしょう。

前もって感謝します..

4

7 に答える 7

44

最初に、静的グローバル変数は、変数をファイルのスコープに制限していることを除いて、グローバル変数と同じです。externつまり、このグローバル変数をキーワードを介して他のファイルで使用することはできません。

したがって、質問をグローバル変数と揮発性変数に減らすことができます。

今揮発性に:

のようconstに、volatileは型修飾子です。

このvolatileキーワードは、特に非同期イベントがある場合に、コードが正しくなくなる可能性のあるコンパイラの最適化を防ぐために作成されました。

として宣言されたオブジェクトはvolatile、特定の最適化では使用されない場合があります。

前の命令が同じオブジェクトから値を要求した場合でも、システムは常に、使用された時点で揮発性オブジェクトの現在の真の値を読み取ります。また、オブジェクトの値は割り当て直後に書き込まれます。これは、揮発性変数をCPUレジスタにキャッシュしないことを意味します。

Jobb博士には、揮発性に関するすばらしい記事があります。

Jobb博士の記事の例を次に示します。

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

コンパイラがそれSleep()が外部呼び出しであると判断したSleep()場合、変数flag_の値を変更できない可能性があると想定します。flag_したがって、コンパイラはの値をレジスタに格納する場合があります。そしてその場合、それは決して変わらないでしょう。ただし、別のスレッドがウェイクアップを呼び出した場合、最初のスレッドは引き続きCPUのレジスタから読み取っています。Wait()目覚めることはありません。

では、変数をレジスターにキャッシュせず、問題を完全に回避しないのはなぜですか?この最適化により、全体として多くの時間を節約できることがわかりました。したがって、C / C ++では、volatileキーワードを使用して明示的に無効にすることができます。

上記の事実flag_はメンバー変数であり、グローバル変数(または静的グローバル)ではありません。例の後の説明は、グローバル変数(および静的グローバル変数)を扱っている場合でも正しい理由を示しています。

よくある誤解は、変数を宣言するvolatileだけでスレッドセーフを確保できるというものです。変数に対する操作は、レジスターに「キャッシュ」されていなくても、アトミックではありません。

ポインタ付きの揮発性:

ポインターを使用すると揮発性であり、ポインターを使用するconstのように機能します。

タイプvolatile int *の変数は、ポインターが指す変数が揮発性であることを意味します。

タイプの変数はint * volatile、ポインター自体が揮発性であることを意味します。

于 2008-12-06T15:51:55.073 に答える
21

それらは異なるものです。私は揮発性セマンティクスの専門家ではありません。しかし、ここで説明することは理にかなっていると思います。

グローバル

グローバルとは、問題の識別子がファイルスコープで宣言されていることを意味します。関数(gotoラベルが定義されている場所)、ファイル(グローバル変数が存在する場所)、ブロック(通常のローカル変数が存在する場所)、関数プロトタイプ(関数パラメーターが存在する場所)と呼ばれるさまざまなスコープがあります。この概念は、識別子の可視性を構造化するためにのみ存在します。最適化とは何の関係もありません。

静的

staticはストレージ期間(ここでは説明しません)であり、ファイルスコープの内部リンケージ内で宣言された名前を付ける方法です。これは、1つの変換ユニット内でのみ必要な関数またはオブジェクトに対して実行できます。典型的な例はhelp、受け入れられたパラメーターを出力するmain関数であり、同じ.cファイルで定義された関数からのみ呼び出されます。

C99ドラフトの6.2.2/2 :

オブジェクトまたは関数のファイルスコープ識別子の宣言にストレージクラス識別子staticが含まれている場合、識別子には内部リンクがあります。

内部リンケージとは、識別子が現在の翻訳単位の外部に表示されないことを意味します(上記のhelp機能のように)。

揮発性

揮発性は別のものです:(6.7.3 / 6

揮発性修飾タイプのオブジェクトは、実装に未知の方法で変更されたり、その他の未知の副作用が発生したりする可能性があります。したがって、そのようなオブジェクトを参照する式は、5.1.2.3で説明されているように、抽象マシンのルールに従って厳密に評価されるものとします。さらに、すべてのシーケンスポイントで、オブジェクトに最後に格納された値は、前述の未知の要因によって変更された場合を除き、抽象マシンによって規定された値と一致する必要があります。

volatileこの規格は、冗長になる例(5.1.2.3 / 8)の優れた例を提供します。

実装は、抽象セマンティクスと実際のセマンティクスの間の1対1の対応を定義する場合があります。すべてのシーケンスポイントで、実際のオブジェクトの値は、抽象セマンティクスによって指定された値と一致します。その場合、キーワードvolatile は冗長になります。

シーケンスポイントは、抽象マシンに関する副作用の影響が完了するポイントです(つまり、メモリセル値などの外部条件は含まれません)。との右と左の間、&&関数呼び出しの||;と戻りは、たとえばシーケンスポイントです。

抽象セマンティクスは、特定のプログラム内のコードのシーケンスのみを参照することからコンパイラーが推測できるものです。ここでは、最適化の効果は関係ありません。実際のセマンティクスには、オブジェクトへの書き込みによって行われる副作用の影響(たとえば、メモリーセルの変更)が含まれます。オブジェクトを揮発性として認定するということは、常にオブジェクトの値をメモリから直接取得することを意味します(「未知の要因によって変更された」)。標準ではスレッドについてはどこにも言及されていません。変更の順序や操作の原子性に依存する必要がある場合は、プラットフォームに依存する方法を使用してそれを確認する必要があります。

概要をわかりやすくするために、Intelにはそれに関するすばらしい記事があります

私は今どうすればいい?

ファイルスコープ(グローバル)データを揮発性として宣言し続けます。グローバルデータ自体は、変数の値がメモリに格納されている値と等しくなることを意味するものではありません。また、staticは、オブジェクトを現在の変換ユニット(現在の.cファイルとそれによって#includeされた他のすべてのファイル)に対してローカルにするだけです。

于 2008-12-06T15:35:04.030 に答える
14

「volatile」キーワードは、コンパイラがその変数を含むコードに対して特定の最適化を行わないように指示します。グローバル変数を使用するだけでは、コンパイラがコードを誤って最適化するのを防ぐことはできません。

例:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';

「揮発性」がないと、最初の書き込みが最適化されてしまう可能性があります。

于 2008-12-06T14:21:18.047 に答える
4

volatile キーワードは、変数が決してキャッシュされないようにコンパイラに指示します。それへのすべてのアクセスは、すべてのスレッド間で一貫した値を持つように一貫した方法で行う必要があります。変更をチェックするループがある間に変数の値が別のスレッドによって変更される場合、通常の変数値がある時点でキャッシュされないという保証がないため、変数を揮発性にする必要があり、ループ同じままであると仮定します。

ウィキペディアの揮発性変数

于 2008-12-06T14:21:47.593 に答える
3

私はフリオルの答えを+1します。さまざまな答えに多くの混乱があるように思われるので、いくつかの精度を追加したいと思います。Cの揮発性はJavaの揮発性ではありません。

したがって、最初に、コンパイラはプログラムのデータフローに基づいて多くの最適化を行うことができます。Cの揮発性はそれを防ぎ、毎回その場所に実際にロード/保存することを保証します(たとえば、それを消去するレジスタを使用する代わりに) 。friolが指摘しているように、メモリマップドIOポートがある場合に便利です。

Cの揮発性は、ハードウェアキャッシュやマルチスレッドとは何の関係もありません。これはメモリフェンスを挿入せず、2つのスレッドがそれにアクセスする場合、操作の順序についての保証はまったくありません。ただし、Javaのvolatileキーワードは、まさにそれを実行します。必要な場所にメモリフェンスを挿入します。

于 2008-12-06T15:35:56.510 に答える
3

現在の環境では違いはないかもしれませんが、わずかな変更が動作に影響を与える可能性があります。

  • 異なるハードウェア (より多くのプロセッサ、異なるメモリ アーキテクチャ)
  • 最適化が改善された新しいバージョンのコンパイラ。
  • スレッド間のタイミングのランダムな変動。問題は 1,000 万回に 1 回しか発生しない可能性があります。
  • 異なるコンパイラ最適化設定。

長い目で見れば、最初から適切なマルチスレッド構成を使用する方がずっと安全です。

もちろん、プログラムがマルチスレッドでない場合は問題ありません。

于 2008-12-06T14:20:13.573 に答える
-4

volatile 変数とは、割り当てられた値が一定でないことを意味します。つまり、volatile 変数 "a=10" を含む関数で、関数がその関数の呼び出しごとに 1 を追加している場合、常に更新された値が返されます。 { volatile int a=10; a++; } 上記の関数が何度も呼び出されると、変数 a は 10 に再初期化されず、プログラムが実行されるまで常に更新された値が表示されます。最初の出力 = 10、次に 11、次に 12 など。

于 2011-07-17T15:40:29.330 に答える