最後のキーワード、volatile について説明します。
これを説明するには「Debugモード」と「Releaseモード」について説明しなければなりません。
これまで何も触れてきませんでしたが、Visual studioを何の設定も変えずに使っている場合「Debugモード」になっています。
しかし、「Debugモード」で作ったプログラムの配布は禁止されています。
1つサンプルプログラムを作ります。
プロジェクト名は「project183」、cppファイルは「Source.cpp」とします。
<sample program 183-01>
#include <stdio.h> int main(void) { int data = 5; data = data * 2; printf("data = %d\n", data); return 0; } |
<実行結果>
data = 10 続行するには何かキーを押してください・・・
単純にデータを2倍して表示するプログラムです。
実行した後で、プロジェクトフォルダを見てください。
「Debug」というフォルダがありますので、開いてみましょう。
project183.exe
という実行ファイルがあるはずです。
ダブルクリックすると実行出来ますが、実行しても一瞬で消えてしまいます。
そこれ、次のようにして下さい。
<sample program 183-02>
#include <stdio.h>
int main(void)
{
int data = 5;
data = data * 2;
printf("data = %d\n", data);
getchar();
return 0;
}
|
これで「ビルド」して、「Debug」フォルダの
project183.exe
をダブルクリックしてみて下さい。
・
・
・
<実行結果>
data = 10
※エンターキーで終了します。
この拡張子が exe というファイルは「実行ファイル」とか「ロードモジュール」と呼ばれるものです。
完成したプログラムを配布(実際に使ってもらう)する際には、この「実行ファイル」を使います。
ですが、上にも書いたように「Debugモード」で作った「実行ファイル」は配布出来ません。
実際に配布するには「Releaseモード」で「ビルド」した「実行ファイル」」でなければならないのです。
では、「Releaseモード」への切り替え方法を説明します。
メニューにある「Debug」を「Release」に切り替えれば良いです。
これで、「Releaseモード」になりました。
モードが変わると「コンパイル」と「ビルド」をし直さなければなりません。
※警告レベルなどもデフォルトに戻っています。
「ビルド」した後で、プロジェクトフォルダを確認してみてください。
「Release」フォルダが出来ています。
この中にも
project183.exe
があります。
ダブルクリックすると、
<実行結果>
data = 10
と表示されるはずです。
※エンターキーで終了します。
何が違うのでしょうか?
実際には色々違うのですが、重要な部分だけ書きます。
モードを「Debugモード」に戻してください。
プロジェクトメニュー → project183 のプロパティ を選択します。
プロパティページの「C/C++」を選び、「最適化」をクリックします。
一番上の欄の「最適化」を見ると、「無効(/od)」と書いてあります。
確認したら「キャンセル」ボタンを押して閉じてください。
次にモードを「Releaseモード」に変えてください。
そして同じく、
プロジェクトメニュー → project183 のプロパティ を選択します。
プロパティページの「C/C++」を選び、「最適化」をクリックします。
見ると、「実行速度の最大化(/o2)」と書いてあります。
「Debugモード」では、完成したプログラムを配布出来ないので、最終的には「Releaseモード」にしなければなりません。
しかし、気付かないうちに「最適化」なるものの設定が変わっていました。
これがどのように影響が出るのでしょうか。
次のプログラムを作ってみましょう。
<sample program 183-03>
int main(void) { unsigned int i; for (i = 0; i < 400000000; i++) ; return 0; } |
これは「空ループ」と言われるもので、何の意味もなく「0〜4億」まで数えるというプログラムです。
まずは「Debugモード」で実行してみてください。
・
・
・
私の環境では、実行が終わるまでに1秒くらいかかりました。
では、このまま「Releaseモード」にして実行してみます。
・
・
・
即座に実行が終わりました。
この違いを理解するには「実行速度の最大化」という言葉だけでは足りません。
もう少し突っ込んで説明します。
「Debugモード」にしましょう。
プロジェクトメニュー → project183 のプロパティ を選択します。
「C/C++」メニューから「出力ファイル」を選びます。
「アセンブリの出力」を「アセンブリコードとソースコード(/FAs)」を選びます。
「OK」を押して「ビルド」します。
プロジェクトフォルダの「Debug」フォルダを開きます。
その中に「Source.asm」というファイルがあるはずです。
これを、Visual Studioにドラッグ&ドロップしてください。
コードを見ると、
; 5 : unsigned int i; ; 6 : ; 7 : for (i = 0; i < 400000000; i++) mov DWORD PTR _i$[ebp], 0 jmp SHORT $LN4@main $LN2@main: mov eax, DWORD PTR _i$[ebp] add eax, 1 mov DWORD PTR _i$[ebp], eax $LN4@main: cmp DWORD PTR _i$[ebp], 400000000 ; 17d78400H jae SHORT $LN3@main ; 8 : ; jmp SHORT $LN2@main $LN3@main: |
となっています。
これは、C言語をアセンブリ言語で表したものです。
中身を理解する必要はありませんが、ループするコードが書かれています。
続いて、「Releaseモード」に変えて、同じ事をします。
プロジェクトメニュー → project183 のプロパティ を選択します。
「C/C++」メニューから「出力ファイル」を選びます。
「アセンブリの出力」を「アセンブリコードとソースコード(/FAs)」を選びます。
「OK」を押して「ビルド」します。
プロジェクトフォルダの「Releas」フォルダを開きます。
その中に「Source.asm」というファイルがあるはずです。
これを、Visual Studioにドラッグ&ドロップしてください。
コードを見ると、
; 5 : unsigned int i; ; 6 : ; 7 : for (i = 0; i < 400000000; i++) ; 8 : ; |
ループするためのアセンブリコードが書かれていません。
これは、この部分のコンパイルを「無視した」という事です。
「Debugモード」は、書かれているコードを素直にコンパイルし、そのまま実行します。
「Releaseモード」は、「実行速度の最大化」を考え「空ループ」は「無駄」と判断します。
ですから、0〜4億を数えること無くコンパイル時に「勝手に省略」してしまうのです。
「Debugモード」で正しく動いていても「Releaseモード」では正しく動かないことがあるという事です。
ここで、「volatile」の出番です。
次のようにプログラムを変更します。
<sample program 183-04>
int main(void)
{
volatile unsigned int i;
for (i = 0; i < 400000000; i++)
;
return 0;
}
|
変数宣言の前に「volatile」を付けました。
これで何が変わるのでしょうか。
設定を変えず「Releaseモード」で「ビルド」しましょう。
上と同じく「Source.asm」を確認します。
; 5 : volatile unsigned int i; ; 6 : ; 7 : for (i = 0; i < 400000000; i++) mov DWORD PTR _i$[ebp], 0 cmp DWORD PTR _i$[ebp], 400000000 ; 17d78400H jae SHORT $LN10@main $LL4@main: inc DWORD PTR _i$[ebp] cmp DWORD PTR _i$[ebp], 400000000 ; 17d78400H jb SHORT $LL4@main $LN10@main: |
「Debugモード」とは若干違いますが、ループのコードが書かれています。
「volatile」と言うキーワードは、「変数を最適化の対象から外す」という機能を持っています。
開発時に「Debugモード」で上手く動いていても「Releaseモード」にすると動かなくなる事があります。
色々な要因がありますが「最適化」が自動的に行われている事で動かなくなる事もあります。
なぜか「Releaseモード」にした途端にバグが発生する・・・という経験をした方は、「最適化」も疑って下さい。
そして「volatile」の存在を思い出してください。