web-dev-qa-db-ja.com

constとconst volatileの違い

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

その後、const volatile int temp;
上記のように変数tempを宣言する使用法は何ですか?
const int temp

79
user559208

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

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

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

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

誠実さとは、価値を変えないという約束であり、価値が変わらないということではありません。

24
Alexandre C.

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

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

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

7
push2eject

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

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

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

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

「extern const volatile int real_time_clock;」

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

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

5
user2903536

この記事では、const修飾子とvolatile修飾子を組み合わせるシナリオについて説明します。

http://embeddedgurus.com/barr-code/2012/01/combining-cs-volatile-and-const-keywords/

3
balajimc55

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

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

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

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

const tempに値(少なくとも0と異なる場合)を宣言すると、コンパイラは変数をRAMアドレスに割り当てられたとしても、FLASH空間のアドレスに割り当てます。すべての操作は読み取り専用であるため、変数の初期値を保存するためにフラッシュメモリが必要です。RAMアドレスはスペースの無駄になります。

結果として:

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

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 temp ISがvoltaileを宣言した場合、IDの変更はすぐに有効になります。

3
Michael Kusch

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

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

プログラムで変数を変更したくない場合は、変数に「const」キーワードを使用します。一方、変数 'c​​onst volatile'を宣言すると、プログラムに変数を変更しないように指示し、コンパイラに、この変数は外部からの入力から予期せず変更できることを伝えます。

1
Ali

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

1
rajeshsam