constとconstvolatileの違い


89

volatile新しい値が更新されるたびに変数を宣言する場合変数をとして
宣言するconst場合、その変数の値は変更されません

それでは、上記のようにconst volatile int temp;
変数を宣言することの使用法は何tempですか?
として宣言するとconst int tempどうなりますか?


あなたは使用することはありませんconst volatile int temp;ブロックスコープ(すなわち内部に{ }それはそこには、使用を持っていません、)。
MM

回答:


145

としてマークされたオブジェクトはconst volatile、コードによる変更が許可されません(const修飾子が原因でエラーが発生します)-少なくともその特定の名前/ポインターを介して。

volatile修飾子の一部は、コンパイラーがオブジェクトへのアクセスを最適化または並べ替えることができないことを意味します。

組み込みシステムでは、これは通常、ハードウェアによって読み取りおよび更新できるハードウェアレジスタにアクセスするために使用されますが、書き込みには意味がありません(または書き込みエラーになる可能性があります)。

例として、シリアルポートのステータスレジスタがあります。さまざまなビットは、文字が読み取られるのを待っているかどうか、または送信レジスタが新しい文字を受け入れる準備ができているかどうか(つまり、空であるかどうか)を示します。このステータスレジスタを読み取るたびに、シリアルポートハードウェアで他に何が発生したかによって、異なる値になる可能性があります。

(特定のハードウェア仕様に応じて)ステータスレジスタに書き込むことは意味がありませんが、レジスタの各読み取りがハードウェアの実際の読み取りになることを確認する必要があります-前の読み取りからのキャッシュ値を使用して ' tハードウェア状態の変化について教えてください。

簡単な例:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg;  //   correct hardware addresses


#define UART_CHAR_READY 0x00000001

int get_next_char()
{
    while ((*status_reg & UART_CHAR_READY) == 0) {
        // do nothing but spin
    }

    return *recv_reg;
}

これらのポインタがであるとマークされていない場合volatile、いくつかの問題が発生する可能性があります。

  • whileループテストは、ステータスレジスタを一度だけ読み取る可能性があります。これは、コンパイラが、ポイントしたものは決して変更されないと想定できるためです(whileループテストまたはループ自体には、変更できるものはありません)。UARTハードウェアで待機している文字がないときに関数を入力すると、文字を受信して​​も停止しない無限ループが発生する可能性があります。
  • 受信レジスタの読み取りは、コンパイラによってwhileループの前に移動される可能性があります-*recv_regループによって変更されたことを示す関数には何もないため、ループに入る前に読み取ることができない理由はありません。

volatileこれらの最適化は、コンパイラによって実行されていないことを修飾子を確実にします。


5
説明のために+1。そして私は質問があります:constvolatileメソッドはどうですか?多くのスレッドによってアクセスされるクラスがある場合(アクセスはミューテックスと同期されますが)、constメソッドも揮発性である必要があります(一部の変数は他のスレッドによって変更される可能性があるため)
Sasa

39
  • volatile 変数に関連するコードを最適化しないようにコンパイラーに指示します。通常は、別のスレッドなどによって「外部」から変更できることがわかっている場合です。
  • const プログラムが変数の値を変更することは禁止されていることをコンパイラーに通知します。
  • const volatileこれは非常に特別なことであり、おそらくあなたの人生(tm)で正確に0回使用されていることがわかります。当然のことながら、プログラムは変数の値を変更できませんが、値は外部から変更できるため、変数に対して最適化は実行されません。

12
volatile変数は通常、他のスレッドではなく、ハードウェアをいじり始めたときに起こることだと思いました。私がconst volatile使用しているのは、メモリマップドステータスレジスタなどです。
ちょうど私の正しい意見2011年

2
もちろん、あなたは絶対に正しいです。マルチスレッドはほんの一例ですが、それだけではありません:)。
mingos

25
組み込みシステムで作業している場合、これは非常に頻繁に見られます。
ダニエルグリロ2013年

28

変数がconstであるため、2つのシーケンスポイント間で変更されていない可能性があります。

恒常性とは、値が変更されないということではなく、値を変更しないという約束です。


9
それを指摘するためのプラス1 constデータが「一定」ではない。
ボグダンアレクサンドル

7

ブートローダーで更新できるフラッシュメモリの領域にいくつかの構成変数が配置されている組み込みアプリケーションでこれを使用する必要がありました。これらの構成変数は実行時に「一定」ですが、揮発性修飾子がないと、コンパイラーは次のようなものを最適化します...

cantx.id = 0x10<<24 | CANID<<12 | 0;

...定数値を事前計算して即時アセンブリ命令を使用するか、近くの場所から定数をロードして、構成フラッシュ領域の元のCANID値への更新が無視されるようにします。CANIDはconstvolatileである必要があります。


7

Cでは、constとvolatileは型修飾子であり、これら2つは独立しています。

基本的に、constは、値がプログラムによって変更できないことを意味します。

また、揮発性とは、値が突然変更される可能性があることを意味します(おそらくプログラムの外部から)。

実際、C標準では、constとvolatileの両方である有効な宣言の例に言及しています。例は

「externconstvolatile intreal_time_clock;」

ここで、real_time_clockはハードウェアによって変更可能ですが、割り当て、インクリメント、またはデクリメントすることはできません。

したがって、constとvolatileを別々に扱う必要があります。さらに、これらの型修飾子は、struct、union、enum、およびtypedefにも適用されます。


5

constとvolatileを一緒に使用できます。たとえば、0x30が外部条件によってのみ変更されるポートの値であると想定される場合、次の宣言は偶発的な副作用の可能性を防ぎます。

const volatile char *port = (const volatile char *)0x30;

4

const変数が変更できないということではなく、cコードで変数を変更できないことを意味します。これは、命令が変数に書き込むことができないが、その値が変更される可能性があることを意味します。

volatile変数はいつでも変更される可能性があるため、キャッシュされた値は使用されない可能性があることを意味します。変数への各アクセスは、そのメモリアドレスに対して実行する必要があります。

質問には「埋め込み」のタグが付けられているので、 temp、ハードウェア関連のレジスタではなく、ユーザーが宣言した変数であるいるため(これらは通常、別の.hファイルで処理されるため)、次のことを考慮してください。

揮発性の読み取り/書き込みデータメモリ(RAM)と不揮発性の読み取り専用データメモリの両方を備えた組み込みプロセッサ。たとえば、データとプログラムスペースが共通のデータとアドレスバスを共有するフォンノイマンアーキテクチャのフラッシュメモリ。

あなたが宣言した場合 const temp値を持つした場合(少なくとも0と異なる場合)、コンパイラは変数をFLASHスペースのアドレスに割り当てます。これは、RAMアドレスに割り当てられた場合でも、初期値を格納するためにFLASHメモリが必要になるためです。すべての操作が読み取り専用であるため、RAMアドレスをスペースの浪費にします。

結果として:

int temp;RAMに格納されている変数で、起動時(cstart)に0に初期化され、キャッシュされた値を使用できます。

const int temp;(read-ony)FLASHに格納され、コンパイラ時に0に初期化される変数であり、キャッシュされた値を使用できます。

volatile int temp; RAMに格納され、起動時に0に初期化される変数(cstart)であり、キャッシュされた値は使用されません。

const volatile int temp; (read-ony)FLASHに格納され、コンパイラ時に0に初期化される変数であり、キャッシュされた値は使用されません

ここに便利な部分があります:

現在、ほとんどの組み込みプロセッサには、特別な汎用モジュールを使用して読み取り専用の不揮発性メモリを変更する機能があります。この場合const int temp、直接ではなく、実行時に変更できます。別の言い方をすれば、関数tempは、格納されているアドレスの値を変更する場合があります。

実用的な例はtemp、デバイスのシリアル番号に使用することです。組み込みプロセッサが最初に実行tempされるときは、0(または宣言された値)に等しくなり、関数はこのファクトを使用して本番環境でテストを実行し、成功した場合はシリアル番号の割り当てを要求し、の値を変更します。tempによって特別な機能の。一部のプロセッサには、そのためだけにOTP(ワンタイムプログラマブル)メモリを備えた特別なアドレス範囲があります。

しかし、ここに違いがあります。

場合はconst int temp、変更IDの代わりに、ワンタイムプログラマブルシリアル番号であると宣言されていないvolatile、キャッシュされた値は、新しいIDは、次の再起動、またはさらに悪いことに、いくつかの機能の有効なそれまではないかもしれないという意味、それまでは次の起動時に使用されるかもしれません再起動するまで、新しい値を使用する場合もあれば、古いキャッシュ値を使用する場合もあります。場合const int tempISが宣言voltaile、IDの変更はすぐに反映されます。


うわー、この答えは長いです


2

簡単に言うと、「const volatile」変数の値はプログラムで変更することはできませんが、ハードウェアで変更することはできます。ここでの揮発性は、コンパイラの最適化を防ぐことです。


1

プログラムで変数を変更したくない場合は、変数に「const」キーワードを使用します。一方、変数 'c​​onst volatile'を宣言すると、プログラムとコンパイラに、この変数が外部からの入力から予期せず変更される可能性があることを通知します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.