★残りの命令(volatile)★


最後のキーワード、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モードへの切り替え


では、「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」の存在を思い出してください。


次へ

戻る

目次へ