質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

6回答

92391閲覧

C言語 volatileの意味がよく分かりません。

hukusama

総合スコア31

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

4グッド

6クリップ

投稿2016/07/04 00:48

C言語の勉強をやり始めて二か月の者です。
volatileの意味が分からないのですが、分かりやすい具体例とかありましたら、よろしければ
教えて頂きたいです。
参考書やネットを使って調べているのですが、なかなか理解できません。

以上、宜しくお願い致します。

Zuishin, Chironian, koach, mondaminZ👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ベストアンサー

volatileの意味が分からないのですが、分かりやすい具体例とかありましたら、よろしければ

教えて頂きたいです。

あなたが想定する対象環境により回答が変わります:

  • 組み込み系の場合:IOポートなど外部デバイスと通信するケースで、メモリアドレスを介して入出力を行うときにvolatileが必要となります。
  • 上記以外の普通のOSの場合:(少々乱暴な言い方ですが)volatileの正しい意味を理解するまでは、それを使わないでください。通常のプログラムでは必要にならない機能です。

また、Intenet上には「マルチスレッドプログラムではvolatileが必要だ」という情報もいくつかみられますが、C言語においては誤った情報です。

  • C言語ではなくJava言語の話をしている可能性があります。CとJavaではvolatileは意味が全く異なります。(=Javaでは正しい情報ですが、Cには適用できない)
  • 古いC言語仕様と古いコンパイラでは、volatileが必要な時代もありました。(=当時は正しかったが、今となっては陳腐化してしまった)

自分向けに書いた内容ですが volatile変数とマルチスレッドとの関係 で詳しく解説しています。(他人に説明するトーンで書いてないので全体的に言葉足らずですが)

投稿2016/07/04 09:18

編集2016/07/04 09:22
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/07/04 10:07 編集

なるほど。この辺の議論は知りませんでした。 異なるCPUにも対応するという意味のマルチプラットフォームなプログラムではvolatileの出番はほとんどなさそうですね。 I/Oアクセスはありえないですし、スレッド間同期もマルチCoreシステムでCPUキャッシュを考えると危険ということですから。奥が深い。 (ところで、yohhoyさんのサイトで順序保証されない例として最適化を挙げられてますが、その場合I/Oアクセス順序も保証されないことにならないでしょうか? 可視性の保証の例に挙げられているように、CPUキャッシュの影響で順序保証されないなら理解できます。I/Oアクセスに関するそれはハードウェア設計者が保証するべきことですから。)
yohhoy

2016/07/04 10:29

Chironianさん: volatile変数へのアクセス=I/Oアクセス同士については、どんなに最適化が行われても順序が維持されます(Cコンパイラは順序保持する責任があります)。この意味で「volatile=最適化の抑止」と簡単に表現されることがよくありますし、シングルスレッドなプログラムに限れば技術的にも正しい主張ではあります。
Chironian

2016/07/04 12:04

yohhoyさん。 ありがとうございます。やはりvolatileをつけることで最適化による順序性の破壊は発生しないのですね。 ただし、volatileでは、CPUキャッシュの同期バラツキには対応できないので、そのようなCPUで走る可能性のあるプログラムのスレッド間同期に使うと危険ということですね。
hukusama

2016/07/05 01:46

yohhoyさん。 とても分かりやすいご説明ありがとうございました。
guest

0

volatile の英語の意味は「気まぐれな、移り気な」(http://eow.alc.co.jp)があるそうです。
つまり、「値が勝手に変わってしまう」変数に対して volatile を宣言します。
「値が勝手に変わってしまう」って、そんなことあるの?あったらプログラムは正しく動かないんじゃ?
と思うでしょうが、あるんです。
典型的には組み込みのシステムで使われる、機械の状態を示す変数(レジスターなどと呼ばれます)です。
例えば、
「ボタンを押している/離している」を「押している = 1」、「離している = 0」とすると、
人間がボタンを操作するごとに、プログラムとは無関係に値が変わります。

この時、ttyp03 さんの回答のように、最適化されていると、0 → 1 の変化をプログラムが
検出できません。
このような事が起こらないように volatile とします。

投稿2016/07/04 01:23

nob.

総合スコア711

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

解決済みになっていますので、蛇足になるかと思いますが、volatile は割り込みハンドラを使ったプログラミングでも使用します。

C

1// SIGTERM の割り込みハンドラに terminate() を設定 2.. 3volatile int shutdown = 0; 4while(shutdown == 0){ 5 処理 6} 7 8// シャットダウン割り込みハンドラ 9void terminate(){ 10 if (shutdown == 0) { 11 //終了処理 12 shutdown = 1; 13 } 14}

というような場合に変数 shutdown を volatile 宣言しておかないと、割り込みハンドラの中で shutdown を正しく参照できません。理由はここまでの議論の通り、処理の実行中に割り込みが発生してハンドラの中から shutdown を参照してもコンパイラの最適化で shutdown の値がメモリに書き込まれていない可能性があるからです。

投稿2016/07/13 01:51

mit0223

総合スコア3401

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

volatile 理解するの苦しみますよね。

結論から言うと volatileは コンパイラ最適化オプションを有効にした上で
最適化対象としたくない変数操作がある変数に対して適用します。

以下、補足説明です。
コンパイラには 最適化オプションというのがあって、
最適化が有効になっていると、定義した変数を利用しないと、不要なコードとして取り扱われ
アセンブラレベルのコードが生成されなくなるケースがあるのです。

例えば

int *a = (int *)0x100000; // 読み対象レジスタのアドレスを0x100000と仮定 void func() { int *b; b = a; // #1 /* 変数bを利用するコードを書かない */ }

上記のサンプルは、変数bが参照されないので、最適化オプションが有効時は、
#1に相当するアセンブラコードが生成されません。
(アセンブラコードを生成するにはお使いのコンパイラのオプションを調べてください)

※ 最適化オプションが無効な場合は、#1に相当するアセンブラコードも生成されます。

ところが、
変数に値を代入する操作(具体的にリード)だけでも意味をもつ場合あります。
特に組込プログラミングにおいては レジスタ値を読むということだけでも意味がある場合があります。

その場合は次の様に記述することになります。

int *a = (int *)0x100000; // 読み対象レジスタのアドレスを0x100000と仮定 void func() { volatile int *b; b = a; // #1 /* 変数bを利用する事がない */ }

上記の場合、最適化オプションが有効な場合でも#1に相当するアセンブラコードは生成されます。

常に最適化オプション無効としてコンパイルするならこんな考慮は必要ないのですが
組込プログラミングにおいては、対象となるハードウエアはPC程の多くのメモリを積んでいないので
可能な限りメモリ使用量を減らしたいケースが殆どなので、
最適化オプション有効+必要に応じてvolatile宣言
というケースが多いです。

ちょっと長くなりましたが、ポイントつかんで頂けたでしょうか。

投稿2016/07/04 02:15

編集2016/07/04 03:01
koach

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

volatile修飾子とはコンパイラによる最適化を抑制するための修飾子です。
最適化というのは無駄がコードを無駄のないコードに置き換えることです。たとえば以下のような変数xに1を足す処理を3回繰り返すコードがあるとします
int x = 0;
x += 1;
x += 1;
x += 1;

しかしこれはxに1回3を足したコードと同じなのでコンパイラは
int x = 0;
x += 3に置き換えてくれます。

そこでvolatileを付けると最適化をさせずに1を3回足したコードのままコンパイルしてくれるようになります。

普通なら最適化してくれた方がありがたいので使いどころが無いように見えますが、最適化されて困る場合もあるのでそのときに使います。

例えばまだご存じないかも知れませんが、マルチスレッドで並列処理をするとき片方の処理が終わるのを待つときこのように書く場合があります。

while(wait){
Sleep(1);
}

//別スレッド
void another_thread(){
//何か処理
wait = false;
}

これはwhile文とanother_thread()関数は同時に動いている状況です。
このときwhile文の方はwaitがtrueであるときループを抜けずに永久にループし続けます。
そして裏で動いているanother_thread()関数でwaitがfalseになるとループを抜けて動き出します。
このようにしてanother_thread()関数内で処理が終わるまで確実に待つことができるのです。

さて本題ですがコンパイラからするとこのコードはwhile文内でwaitは一度も変化しません。
while(wait){
Sleep(1);
}
しかしwhile文は毎回分岐処理をしますので1度も変化のない変数を毎回判定するのは無駄と考えます。
すると以下のコードに書き換えます
if(wait){
while(1){
Sleep(1);
}
}

最初に1回だけif文で判定してその後はwhile(1)で永久にループする無駄のないコードになりました。
しかしこれは意図通りの動きはしてくれませんね、裏でwaitを書き換えてもループを抜けることができません。
そこでwaitにvolatileを付けることで、この変数は最適化しなくてもいいよとコンパイラに教えることで解決することができるようになります。

そのほかにも組み込みなんかでレジスタではなく確実にメモリに値を書き込んでほしいときにも使ったりします。
まあこれらは今後使うようになるかもしれないことなので今は頭の片隅に置いといてください。

投稿2016/07/04 01:48

stfl

総合スコア19

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2016/07/04 02:06

同期プリミティブとしてはもっと適切なものがありますので、(環境がそれを許すと保証してくれている状況でなければ)volatileを同期用に使うべきではありません。 https://www.jpcert.or.jp/sc-rules/c-pos03-c.html
stfl

2016/07/04 02:30

規格にあまり詳しくなかったので勉強になりました。 ありがとうございます。
guest

0

簡単に言えばコンパイラによる最適化を抑止するでしょうか。
下記サイトにはわかりやすい例が載っていました。
http://proger.blog10.fc2.com/blog-entry-20.html

投稿2016/07/04 01:04

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2016/07/04 09:06

言及されているサイトの内容は、(2011年以降のC言語としては)誤りを含んでいます。 簡単な表現では「最適化の抑止」でもよいのですが、volatileは組み込み系でみられるメモリマップドI/Oを考慮したものであり、一般的なOSでいうスレッド間の同期には利用できません(してはいけません)。
hukusama

2016/07/05 01:10

yohhoyさん。 ご親切にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問