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

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

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

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

Visual Studio

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

C++

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

Q&A

解決済

5回答

7210閲覧

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

shell

総合スコア13

MFC

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

Visual Studio

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

C++

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

0グッド

1クリップ

投稿2017/05/15 10:25

編集2017/05/15 14:06

前提・実現したいこと

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

cpp

1// 引数に与えた値の中から最大値を取得する可変長template関数 2template<typename Ty> 3Ty GetMax(const Ty& left, const Ty& right){ 4 return (left < right) ? right : left; 5} 6 7template<typename Ty, typename... Ts> 8Ty GetMax(const Ty& num, const Ty& num2, const Ts&... nums){ 9 return GetMax(GetMax(num, num2), nums...); 10} 11 12//実装コード 13CString str(_T("hoge")); 14CString str2(_T("dog")); 15CString str3(_T("be")); 16 17int max_length = GetMax(str.GetLength(), str2.GetLength(), str3.GetLength());

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

  • 補足

一度変数に代入するという回避策を頂きました。
例えば以下のような感じです。

cpp

1CString str(_T("hoge")); 2CString str2(_T("dog")); 3CString str3(_T("be")); 4int size1 = str.GetLength(); 5int size2 = str2.GetLength(); 6int size3 = str3.GetLength(); 7 8int 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 )
  • リンカー / 最適化オプションが以下の通りです
項目DebugRelease
参照なしはい(/OPT:REF)
COMDATの圧縮なしはい(/OPT:ICF)

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

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

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

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

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

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

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

t_obara

2017/05/15 11:35

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

2017/05/15 14:01

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

回答5

0

ベストアンサー

こんにちは。

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

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

stdafx.h

C++

1(前略) 2#include <sstream> 3 4#ifdef _UNICODE 5#if defined _M_IX86 6#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 7#elif defined _M_X64 8#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 9#else 10#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 11#endif 12#endif

MainFrm.cpp

C++

1(前略) 2// 引数に与えた値の中から最大値を取得する可変長template関数 3template<typename Ty> 4Ty GetMax(const Ty& left, const Ty& right){ 5 return (left < right) ? right : left; 6} 7 8template<typename Ty, typename... Ts> 9Ty GetMax(const Ty& num, const Ty& num2, const Ts&... nums){ 10 return GetMax(GetMax(num, num2), nums...); 11} 12 13 14CMainFrame::CMainFrame() 15{ 16 // TODO: メンバー初期化コードをここに追加してください。 17 theApp.m_nAppLook = theApp.GetInt(_T("ApplicationLook"), ID_VIEW_APPLOOK_VS_2008); 18 //実装コード 19 CString str(_T("hoge")); 20 CString str2(_T("dog")); 21 CString str3(_T("be")); 22 23 int max_length = GetMax(str.GetLength(), str2.GetLength(), str3.GetLength()); 24 std::stringstream ss; 25 ss << max_length; 26 MessageBoxA(NULL, ss.str().c_str(), "title", MB_OK); 27} 28(後略)

投稿2017/05/15 13:50

Chironian

総合スコア23272

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

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

shell

2017/05/15 14:13

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

2017/05/15 15:03

補足コードはパラメータに「左辺値」を与えています。 元のコードは「右辺値」です。この2つは意外に差が大きいです。 「右辺値」は「値」であり変数ではありませんので、本来参照することはできません。 しかし、const参照は「右辺値」を受け取れることになっていますので、ご提示のコードで本来問題は出ないはずです。 そのあたりのVisual C++の実装に何か不具合がある可能性もありますが、私の手元では再現しないので、何かコンパイラ・オプションの指定に差があるのかも知れません。なんとなく最適化をやりすぎている可能性が疑わしいように感じます。 私は、プロジェクトから単純にMFCアプリケーションをデフォルトのまま生成し、/OPT:REFと/OPT:ICFをリンカへ与えています。
shell

2017/05/16 00:22

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

0

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

cpp

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

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

補足

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

投稿2017/05/16 00:27

shell

総合スコア13

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

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

0

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

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

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

投稿2017/05/16 00:06

t_obara

総合スコア5488

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

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

0

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

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

投稿2017/05/15 14:24

HogeAnimalLover

総合スコア4830

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

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

shell

2017/05/15 15:02

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

0

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

投稿2017/05/15 14:06

yoorwm

総合スコア1305

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

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

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問