トップ回答者
メモリーリークが発生する

質問
-
C++の勉強を始めたばかりで何が原因で以下のような現象が発生するのかまた、どう対処してよいのかわかりません。お力をお貸しください。
処理内容は.iniファイルからの読み込みです。プログラム終了後に出力ウィンドウに次のようなメッセージが表示されます。
Detected memory leaks!
Dumping objects ->
.\Test_01Dlg.cpp(151) : {127} normal block at 0x003BA078, 512 bytes long.
Data: <D : \ 2 C G L \ > 44 00 3A 00 5C 00 32 00 43 00 47 00 4C 00 5C 00
.\Test_01Dlg.cpp(151) : {124} normal block at 0x003B9DE0, 512 bytes long.
Data: <3 0 > 33 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00
.\Test_01Dlg.cpp(151) : {122} normal block at 0x003B9BA0, 512 bytes long.
Data: <1 9 2 . 1 6 8 . > 31 00 39 00 32 00 2E 00 31 00 36 00 38 00 2E 00
.\Test_01Dlg.cpp(52) : {119} normal block at 0x003B98B0, 512 bytes long.
Data: <c : \ V C + + \ > 63 00 3A 00 5C 00 56 00 43 00 2B 00 2B 00 5C 00
.\Test_01Dlg.cpp(50) : {118} normal block at 0x003B9670, 512 bytes long.
Data: <D : \ 2 C G L \ > 44 00 3A 00 5C 00 32 00 43 00 47 00 4C 00 5C 00
Object dump complete.
プログラム '[5012] Test_01.exe: ネイティブ' はコード 0 (0x0) で終了しました。
指示された行番号を見ると、
50:LPTSTR lpResult = new TCHAR[256];
52:LPWSTR lpPathName = new TCHAR[256];
151:LPTSTR lpTemp = new TCHAR[0x100];
です。
たぶんこれらの変数に値を代入したときに発生していると思われますが不明です。
以上、よろしくお願いいたします。。
回答
-
textってなに?
iniファイルのフルパスとは?
実行ファイルと同じ位置にあるiniファイルであれば、GetcurrentDirectoryから組み立てるのは間違っています。
GetModuleFileNameをつかって、実行ファイル名から取得するようにしましょう。
パスの組み立ては、makepath系もしくはPath~系を使うと便利です。
参考
http://social.msdn.microsoft.com/forums/ja-JP/vcgeneralja/thread/37bb536c-d1df-44e5-ae07-9976bf74faf9/
わんくま同盟[http://blogs.wankuma.com/blue/]- 回答としてマーク sk7474 2009年5月7日 9:45
-
また、「それから、TCHAR[] を LPWSTR に入れるのは避けるべきです。TCHAR が wchar_t とは限りません。」
ダイナミックに論点か変更されているんですが。
ですが、今回下記のようなコードでiniファイルのフルパスを与えるために行っていますが、ほかによい方法がございますでしょうか。
質問の方は蒼の洞窟さんのを参照してもらうとして、私が言いたいのは LPWSTR と TCHAR は互換性の無い(かも知れない)型であるってこと。50 行目や 151 行目では TCHAR 配列を LPWSTR 型じゃない変数に代入してますよね?- 回答としてマーク sk7474 2009年5月7日 9:45
-
deleteし忘れを防ぐためにも変数を別の用途に再利用すべきではありません。
Hongliangさんがアドバイスしていますが、
TCHAR result[256];
WCHAR pathName[256];
TCHAR temp[0x100];
のように配列にすれば解放漏れも防げます。加えて型宣言の誤り(=の左右出方が異なる)も防げます。
ただし配列で多くのメモリ確保をしてしまうとスタックが枯渇することがあるため注意が必要です。
これに対してVisual Studio 2008 SP1以降ではヒープを使う方法が提供されています。
#include <array>
std::tr1::array<TCHAR, 256> lpResult;
もしくはVisual Studioのバージョンに依存しない別の方法として
#include <vector>
std::vector<TCHAR> lpResult( 256 );
とすることもできます。どちらもnewしていませんのでdeleteは必要ありません。
lpResultの型がLPTSTRではなくなりますので、 &lpResult[0] を使用する必要があるかもしれません。- 回答としてマーク sk7474 2009年5月7日 9:45
-
私が言いたいのは LPWSTR と TCHAR は互換性の無い(かも知れない)型であるってこと。
ちょっと追記と言うことで参考までに。
TCHARはプロジェクトのプロパティの全般設定にある文字セットによって、変化します。
(_UNICODEプリプロセッサ定数の定義の有無)
マルチバイト文字セットの場合にはTCHAR型がchar型のtypedefとなり、Unicode文字セットの場合にはTCHAR型がwchar_t型のtypedefになります。
LPWSTR型は実際にはwchar_t*型のtypedefになりますので、Unicode文字セットではTCHAR型で互換性がありますが、マルチバイト文字セットでは互換性がありません。
TCHAR型で確保したのであればLPTSTRあたりを使うべきですし、wchar_t( = WCHAR)型で良いのであれば、LPWSTR型とwchar_t(WCHAR)型を使うべきかと思います。
関連(文字型、そのポインタ、constのついたポインタ)
TCHAR, LPTSTR, LPCTSTR
WCHAR( = wchar_t), LPWSTR, LPCWSTR
CHAR( = char), LPSTR, LPCSTR
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 回答としてマーク sk7474 2009年5月7日 9:45
すべての返信
-
Hongliangさん、ありがとうございました。
言われるようにdeleteとすると、発生しなくなりました。
また、「それから、TCHAR[] を LPWSTR に入れるのは避けるべきです。TCHAR が wchar_t とは限りません。」
ですが、今回下記のようなコードでiniファイルのフルパスを与えるために行っていますが、ほかによい方法がございますでしょうか。
nRet = GetCurrentDirectory(0x100,lpPathName);
lpFileName = lpPathName ;
text = lpPathName;
text += _T("\\DATA\\iniFile\\Config.ini");
lpFileName = text;
です。
以上、よろしくお願いいたします。 -
textってなに?
iniファイルのフルパスとは?
実行ファイルと同じ位置にあるiniファイルであれば、GetcurrentDirectoryから組み立てるのは間違っています。
GetModuleFileNameをつかって、実行ファイル名から取得するようにしましょう。
パスの組み立ては、makepath系もしくはPath~系を使うと便利です。
参考
http://social.msdn.microsoft.com/forums/ja-JP/vcgeneralja/thread/37bb536c-d1df-44e5-ae07-9976bf74faf9/
わんくま同盟[http://blogs.wankuma.com/blue/]- 回答としてマーク sk7474 2009年5月7日 9:45
-
また、「それから、TCHAR[] を LPWSTR に入れるのは避けるべきです。TCHAR が wchar_t とは限りません。」
ダイナミックに論点か変更されているんですが。
ですが、今回下記のようなコードでiniファイルのフルパスを与えるために行っていますが、ほかによい方法がございますでしょうか。
質問の方は蒼の洞窟さんのを参照してもらうとして、私が言いたいのは LPWSTR と TCHAR は互換性の無い(かも知れない)型であるってこと。50 行目や 151 行目では TCHAR 配列を LPWSTR 型じゃない変数に代入してますよね?- 回答としてマーク sk7474 2009年5月7日 9:45
-
deleteし忘れを防ぐためにも変数を別の用途に再利用すべきではありません。
Hongliangさんがアドバイスしていますが、
TCHAR result[256];
WCHAR pathName[256];
TCHAR temp[0x100];
のように配列にすれば解放漏れも防げます。加えて型宣言の誤り(=の左右出方が異なる)も防げます。
ただし配列で多くのメモリ確保をしてしまうとスタックが枯渇することがあるため注意が必要です。
これに対してVisual Studio 2008 SP1以降ではヒープを使う方法が提供されています。
#include <array>
std::tr1::array<TCHAR, 256> lpResult;
もしくはVisual Studioのバージョンに依存しない別の方法として
#include <vector>
std::vector<TCHAR> lpResult( 256 );
とすることもできます。どちらもnewしていませんのでdeleteは必要ありません。
lpResultの型がLPTSTRではなくなりますので、 &lpResult[0] を使用する必要があるかもしれません。- 回答としてマーク sk7474 2009年5月7日 9:45
-
私が言いたいのは LPWSTR と TCHAR は互換性の無い(かも知れない)型であるってこと。
ちょっと追記と言うことで参考までに。
TCHARはプロジェクトのプロパティの全般設定にある文字セットによって、変化します。
(_UNICODEプリプロセッサ定数の定義の有無)
マルチバイト文字セットの場合にはTCHAR型がchar型のtypedefとなり、Unicode文字セットの場合にはTCHAR型がwchar_t型のtypedefになります。
LPWSTR型は実際にはwchar_t*型のtypedefになりますので、Unicode文字セットではTCHAR型で互換性がありますが、マルチバイト文字セットでは互換性がありません。
TCHAR型で確保したのであればLPTSTRあたりを使うべきですし、wchar_t( = WCHAR)型で良いのであれば、LPWSTR型とwchar_t(WCHAR)型を使うべきかと思います。
関連(文字型、そのポインタ、constのついたポインタ)
TCHAR, LPTSTR, LPCTSTR
WCHAR( = wchar_t), LPWSTR, LPCWSTR
CHAR( = char), LPSTR, LPCSTR
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 回答としてマーク sk7474 2009年5月7日 9:45