読者です 読者をやめる 読者になる 読者になる

Bye Bye Moore

猫マンション建築の野望を胸に零細事業主として資本主義の荒波に漕ぎ出したアラサー男の技術メモ

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

C 組み込み

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:実際のぶぶ漬け〜は都市伝説の類らしいですが