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

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

ただいまの
回答率

90.50%

  • C++

    4425questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    2356questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • MFC

    144questions

    MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

C++/MFCでReleaseモード時のみ値が不定になってしまう

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,136

shell

score 5

 前提・実現したいこと

Visual Studio 2013を用いて、C++/MFCアプリケーションを開発しています。
とあるプロジェクトにおいて、Releaseモードのみ発生するバグが見つかりました。
原因を特定した所、Releaseモードにおいて意図しない動作をしているコードが発見でき、それによって引き起こされるバグと判明しました。
以下が、原因箇所のミニマムなコードです。

// 引数に与えた値の中から最大値を取得する可変長template関数
template<typename Ty>
Ty GetMax(const Ty& left, const Ty& right){
    return (left < right) ? right : left;
}

template<typename Ty, typename... Ts>
Ty GetMax(const Ty& num, const Ty& num2, const Ts&... nums){
    return GetMax(GetMax(num, num2), nums...);
}

//実装コード
CString str(_T("hoge"));
CString str2(_T("dog"));
CString str3(_T("be"));

int max_length  = GetMax(str.GetLength(), str2.GetLength(), str3.GetLength());


実行時、max_lengthをキャプチャした所、Debugモードでは4を示し、Releaseモードでは意図しない値(例えば5488576など)が観測されました。

  • 補足
    一度変数に代入するという回避策を頂きました。
    例えば以下のような感じです。
CString str(_T("hoge"));
CString str2(_T("dog"));
CString str3(_T("be"));
int size1 = str.GetLength();
int size2 = str2.GetLength();
int size3 = str3.GetLength();

int max_length  = GetMax(size1, size2, size3); //4が入る


これだと確かにコンパイルは通り、実行結果も上手く行きます。
しかし、何故直接では駄目で一度変数に逃がす必要があるのかわからないので、依然として納得できません。

 疑問

  • まず何故このような結果になってしまうのか。
  • Debugモード / Releaseモードで挙動が違う?検討が付きません。
  • 補足コードのようにすると実行結果は上手くいくが、何故直接GetLength()からでは駄目なのか

 補足情報

  • Windows 7 Pro SP1 64bit
  • Visual Studio 2013( Version 12.0.40629.00 Update 5 )
  • リンカー / 最適化オプションが以下の通りです
項目 Debug Release
参照 なし はい(/OPT:REF)
COMDATの圧縮 なし はい(/OPT:ICF)

上記以外で必要な情報があれば追記します。

分かる方いらっしゃったら、回答の方よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • t_obara

    2017/05/15 20:35

    GetLengthの戻りを変数に格納してから呼び出しても症状は変わりませんか?

    キャンセル

  • shell

    2017/05/15 23:01

    ありがとうございます。戻り値を変数に格納すると現象は改善したのですが、何故一度格納する必要があるのか、直接代入では駄目なのかが疑問に思います。

    キャンセル

回答 5

checkベストアンサー

+2

こんにちは。

Visual Studio 2013 Update 5でやってみましたが再現しませんでした。
Release/Debug x 32bitビルド/64ビットビルドの全てで4になりました。
コード的にも問題なさそうに感じます。

max_lengthの内容はどのようにして確認されましたでしょうか?
私は下記コードを書きました。(MFCはほとんど触ったことがないので、最初に目についたそれっぽいソースに放り込んでます。無茶でごめんなさい。)

stdafx.h

(前略)
#include <sstream>

#ifdef _UNICODE
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#endif

MainFrm.cpp

(前略)
// 引数に与えた値の中から最大値を取得する可変長template関数
template<typename Ty>
Ty GetMax(const Ty& left, const Ty& right){
    return (left < right) ? right : left;
}

template<typename Ty, typename... Ts>
Ty GetMax(const Ty& num, const Ty& num2, const Ts&... nums){
    return GetMax(GetMax(num, num2), nums...);
}


CMainFrame::CMainFrame()
{
    // TODO: メンバー初期化コードをここに追加してください。
    theApp.m_nAppLook = theApp.GetInt(_T("ApplicationLook"), ID_VIEW_APPLOOK_VS_2008);
    //実装コード
    CString str(_T("hoge"));
    CString str2(_T("dog"));
    CString str3(_T("be"));

    int max_length = GetMax(str.GetLength(), str2.GetLength(), str3.GetLength());
    std::stringstream ss;
    ss << max_length;
    MessageBoxA(NULL, ss.str().c_str(), "title", MB_OK);
}
(後略)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/15 23:13

    Chironain様、回答ありがとうございます。

    確認方法はReleaseモード時、わざと問題箇所で一度プログラムを中断するようにブレークポイントを入れ、ウォッチ式を使いました。
    最初ウォッチ式はReleaseモード時使っては行けないものなのか、とも思いましたが
    上記補足コードで追加したように、一度変数に逃がす上手く処理が行くものは4が代入されていることをDebug/Release両方で同様の方法で確認できました。
    しかし、直接`GetLength()`から取得する問題のコードでは、依然として不定値のような状態のままです。

    キャンセル

  • 2017/05/16 00:03

    補足コードはパラメータに「左辺値」を与えています。
    元のコードは「右辺値」です。この2つは意外に差が大きいです。
    「右辺値」は「値」であり変数ではありませんので、本来参照することはできません。
    しかし、const参照は「右辺値」を受け取れることになっていますので、ご提示のコードで本来問題は出ないはずです。

    そのあたりのVisual C++の実装に何か不具合がある可能性もありますが、私の手元では再現しないので、何かコンパイラ・オプションの指定に差があるのかも知れません。なんとなく最適化をやりすぎている可能性が疑わしいように感じます。

    私は、プロジェクトから単純にMFCアプリケーションをデフォルトのまま生成し、/OPT:REFと/OPT:ICFをリンカへ与えています。

    キャンセル

  • 2017/05/16 09:22

    ありがとうございます。
    おっしゃる通り「左辺値」「右辺値」の差異によってVisual C++の不具合がある。可能性を考慮し
    かつ最適化の可能性を疑いました。
    結果、最適化を局所的に外すことで該当現象は解決されたので、一旦これでクローズしたいと思います。

    キャンセル

+1

Chironian様の指摘で、最適化のしすぎの可能性を疑いました。
そこで該当処理実装箇所を、以下のように最適化を抑制しました。

#pragma optimize("",off)
void function(){
//...その他の処理
int max_length  = GetMax(str.GetLength(), str2.GetLength(), str3.GetLength());
//...その他の処理
}
#pragma optimize("",on)


これにより、該当現象は解決されました。

 補足

Visual Studio 2015、Visual Studio 2017のいずれも該当現象が発生せず、
今回手元にあったVisual Studio 2013でのみの現象でした。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

GetLength()の値次第で、返り値の型が変わるのではないでしょうか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/15 23:15

    https://msdn.microsoft.com/ja-jp/library/ms928945.aspx
    によれば、`GetLength()`の返り値はint以外になさそうです。

    キャンセル

0

引用テキスト以下が、原因箇所のミニマムなコードです。

この手のバグは、「原因箇所のミニマムなコード」という絞り込みがそもそも間違いであるケースがあります。「正常な箇所で発現するが、バグの原因箇所は全く別の場所(患部では潜在するだけで正常な場所で顕在化するケース)」ということもありますので注意ください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/16 00:02

    回答ありがとうございます。
    十分その点を考え、精査し現状のミニマムコードでバグ現象と辻褄が合う挙動を示したので今回の質問に至った次第です。
    ただし、他に原因がある可能性は0ではないので指摘いただいた点は留意しています。

    キャンセル

0

GetLengthの戻りを変数に格納してから呼び出しても症状は変わりませんか?
ありがとうございます。戻り値を変数に格納すると現象は改善したのですが、何故一度格納する必要があるのか、直接代入では駄目なのかが疑問に思います。

GetLengthの実装次第に思いますが、GetLengthの戻り値を直接参照としてGetMaxに引き渡しています。
この際に、GetLengthのスコープを外れた際に、参照している変数の内容が破壊され、GetMaxを処理する際に
想定外の結果が得られるのではないかと思われます。
そして、GetLengthの戻り値を変数に格納することで、参照している変数が破壊されず、問題なく処理ができて
いるのではないかと思われます。
また、破壊されるか否かは、Releaseで発生しやすいが、必ず破壊されるかは実行時の環境などに依存する可能性があると思われるため、プロジェクトを作成し直して試すと再現しなくなる可能性もあるかと。

GetLengthの実装をご覧いただいたり、GetMaxで処理する前の引数の値を印字してみるとかすると良いかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • C++

    4425questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    2356questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • MFC

    144questions

    MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。