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

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

ただいまの
回答率

90.34%

  • C

    4006questions

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

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

解決済

回答 6

投稿

  • 評価
  • クリップ 5
  • VIEW 19K+

hukusama

score 23

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

checkベストアンサー

+5

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

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

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/04 19:04 編集

    なるほど。この辺の議論は知りませんでした。

    異なるCPUにも対応するという意味のマルチプラットフォームなプログラムではvolatileの出番はほとんどなさそうですね。
    I/Oアクセスはありえないですし、スレッド間同期もマルチCoreシステムでCPUキャッシュを考えると危険ということですから。奥が深い。

    (ところで、yohhoyさんのサイトで順序保証されない例として最適化を挙げられてますが、その場合I/Oアクセス順序も保証されないことにならないでしょうか? 可視性の保証の例に挙げられているように、CPUキャッシュの影響で順序保証されないなら理解できます。I/Oアクセスに関するそれはハードウェア設計者が保証するべきことですから。)

    キャンセル

  • 2016/07/04 19:29

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

    キャンセル

  • 2016/07/04 21:04

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

    キャンセル

  • 2016/07/05 10:46

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

    キャンセル

+3

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

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

// SIGTERM の割り込みハンドラに terminate() を設定
..
volatile int shutdown = 0;
while(shutdown == 0){ 
  処理
}

// シャットダウン割り込みハンドラ 
void terminate(){ 
  if (shutdown == 0) {
    //終了処理 
    shutdown = 1;
  }
}

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/04 18:06

    言及されているサイトの内容は、(2011年以降のC言語としては)誤りを含んでいます。

    簡単な表現では「最適化の抑止」でもよいのですが、volatileは組み込み系でみられるメモリマップドI/Oを考慮したものであり、一般的なOSでいうスレッド間の同期には利用できません(してはいけません)。

    キャンセル

  • 2016/07/05 10:10

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

    キャンセル

+1

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 11:06

    同期プリミティブとしてはもっと適切なものがありますので、(環境がそれを許すと保証してくれている状況でなければ)volatileを同期用に使うべきではありません。

    https://www.jpcert.or.jp/sc-rules/c-pos03-c.html

    キャンセル

  • 2016/07/04 11:30

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

    キャンセル

+1

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宣言
というケースが多いです。

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    4006questions

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