Bye Bye Moore

PoCソルジャーな零細事業主が作業メモを残すブログ

volatileの使い道 コンパイラを黙らせる

volatileはお節介焼きのコンパイラにぶぶ漬けをお出しする修飾子です。*1

たとえば、以下のような処理をしたいとします

  • 書き込み先:0x12345678 (外部インタフェイスへのアドレス)
  • 書き込むもの:(順番に)0x31,0x32,0x33 //ASCIIの'1','2','3'

この内容ならポインタ使えばいーじゃん楽勝じゃーんと

unsigned char*         UART0_THR = (unsigned char*)0x12345678 ;

*UART0_THR = 0x31 ;          // '1'
*UART0_THR = 0x32 ;          // '2'
*UART0_THR = 0x33 ;          // '3'

などとやると、UART0_THRには'3'しかいかない「こともあります」。もしくは順番が入れ替わったり。
……すべてコンパイラのお節介が原因です。

例えば、起動コマンド等々でこの順番に値を入れないけない場合……知らなければ原因不明で詰みますね。
そんなあなたに、volatile。
講義ではまず見かけない、習ったとしても音速で忘れ去れれる子ですが……組み込み野郎にとっては背中を任せられる戦友です。

無難な記述法

毎回、間接参照演算子をつかって代入する方法です。
可読性が犠牲にならず、かつ「お節介」を阻止できます。

//最適化阻止のため、volatile化。
//(多少面倒でも、識別しやすい名前を付けること。)
volatile unsigned char*         UART0_THR = (volatile unsigned char*)0x12345678 ;
 
//volatileなしだと、一番下の行しか実行されない。最終的に3が入ると認識されるから。
*UART0_THR = 0x31 ;          // '1'
*UART0_THR = 0x32 ;          // '2'
*UART0_THR = 0x33 ;          // '3'

お上品でない方法その1 入れ子ポインタ

以下は、可読性の問題から「お上品でない」と言われるコードです。
次の例は、入れ子ポインタです。パッと見、わけがわかりません。
一回しか使わない、あるいはC言語コンテストでアスキーアートする必要性に駆られた場合はアリかもしれません。

 *((volatile unsigned char*)0x12345678) = 0x31 ;
お上品でない方法その2 マクロ

次はマクロです。volatileの量が多い場合には、使うと幸せになれます。

//これはただのマクロ
#define   UART0_THR                        (*((volatile unsigned char*)0x12345678))
 
//関数マクロ。邪道なので、可能な限り使わない。
#define   UART0_THR(a)                    (*((volatile unsigned char*)0x12345678)) = a
 
UART0_THR = 0x31 ;
a = UART0_THR ;

//MISRA的には正しくないが、動作完了待ちの場合は以下を入れるのも手。
//というのも、書き込みが完了していなければ(volatileにより)以下の命令は実行されないから。
UART0_THR; //meaning less

と、こんな具合に素敵なシンボルなのです。最適化してほしくない箇所には是非つかってください。
ただ無闇やたらにvolatile化するのも考え物です。
コンパイラは頭がいい人が設計したものなので、基本的に信じて最適化はまかせましょう。
適材適所で使い分けてください。

*1:実際のぶぶ漬け〜は都市伝説の類らしいですが