charの2次元配列のメモリ解放について

[戻る]
HI 2006/01/16(月) 02:20:04
いつも参考にしております。
早速ですが、質問です。

charの2次元配列を生成して、使い終わったら
deleteしているだけなのですが、newした配列の
メモリ解放を行おうとするとデバッグエラーが
出てしまいます。
以下のソースではエラーになるため、配列の解放を
コメントアウトしていますが、これではメモリリーク
してしまいます。
ちなみに他の型(intやlong)では、以下のソースで
エラーは発生しません。
申し訳ありませんが、原因と対策をお教えください。
お願いいたします。


環境
・WinXP Home
・VC++ .net2003

// main の引数を動的配列にコピーし、
// 解放するだけのプログラム
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    New objNew;

    objNew.CopyArg(argc, argv);
    objNew.DeleteArg();

    return 0;
}

int New::CopyArg(int argc, TCHAR** argv)
{
    m_pszArgv = NULL;
    m_iArgc = argc;

    m_pszArgv = new TCHAR* [ m_iArgc ];
    for( int i = 0 ; i < m_iArgc ; i++ ) {
        size_t tLen = _tcslen( argv[i] );
        m_pszArgv[i] = new TCHAR [ tLen ] ;
        _tcscpy(m_pszArgv[i], argv[i]); 
        printf("m_pszArgv[%d] = %s\n", i, m_pszArgv[i]);
    }

    return 0;
}


int New::DeleteArg(void)
{
    // メモリ解放
    for( int i=0; i<m_iArgc; i++ ){
        //delete [] m_pszArgv[i];    // →エラーになるのでコメント
                                     // メモリリークしてしまう!
    }
    delete [] m_pszArgv;

    return 0;
}

Blue 2006/01/16(月) 02:54:24
> size_t tLen = _tcslen( argv[i] );
は文字列の終端である '\0' の領域を含んでいませんので、

size_t tLen = _tcslen( argv[ i ] ) + sizeof( TCHAR );

としてあげましょう。

シャノン 2006/01/16(月) 02:56:03
> size_t tLen = _tcslen( argv[ i ] ) + sizeof( TCHAR );
> としてあげましょう。

tLen は文字数単位なんだから、+1 でいいんじゃない?

Blue 2006/01/16(月) 03:03:46
> tLen は文字数単位なんだから、+1 でいいんじゃない?
ああ、そうでした orz

size_t tLen = _tcslen( argv[ i ] ) + 1;

です。
すいませんでした。+指摘ありがとうございました。

Blue 2006/01/16(月) 03:32:48
ちなみに
_tcsdup
http://www.microsoft.com/JAPAN/developer/library/vccore/_crt__strdup.2c_._wcsdu
p.2c_._mbsdup.htm
ってな便利な関数があります。

ただし、
>  _strdup 関数は malloc 関数を使って string のコピー用の記憶領域を割り当てま
す。
> したがって、必ず _strdup が返したポインタを引数として free ルーチンを呼び出し
て、
> メモリを解放してください。
です。

Ban 2006/01/16(月) 08:32:55
# std::vector<std::string>とかの方が面倒がないような...用途次第ですが。

Blue 2006/01/16(月) 08:59:32
> 用途次第ですが
TCHAR前提だと、ちょっとめんどくさそうかも。
std::basic_string< TCHAR >とか?

std::stringとstd::wstringの切り分けのうまい方法あるんでしょうか>詳しい方

Ban 2006/01/16(月) 10:02:01
namespace win32
{
    typedef std::basic_string<TCHAR>  string;
}
win32::string str;

みたいなのでどうでしょう。
実際、似たようなのを使ったことありますが、
namespace foo = win32;
foo::string str2;
みたいなこともできるはず。

# グローバルで using namespace std; とか書いてる駄目なソースだとちょっと厄介/面倒かも。

επιστημη 2006/01/16(月) 10:37:36
namespace win32 {
  typedef std::basic_string<TCHAR>  string;
}

int _tmain(int argc, TCHAR* argv[]) {
  std::vector<win32::string> args(argv, argv+argc);
  ...

…ってことで。

HI 2006/01/16(月) 23:38:48
[[解決]]
ご回答ありがとうございます。
メモリリークを検出できるという点で、new
を使用したいと思います。
 size_t tLen = _tcslen( argv[ i ] ) + 1;
で問題ないことを確認しました。

STLでも有益な情報ありがとうございます。
これからじっくり試してみようと思います。
みなさん今後ともよろしくお願いします。

Ban 2006/01/17(火) 01:29:03
無理にとめる権利はないのですが、少しだけ。

> メモリリークを検出できるという点で、newを使用したいと思います。

std::vector<win32::string> args(argv, argv+argc);なら、
new のようにプログラマがわざわざメモリリークを意識しなくても、
内部で勝手に管理してくれるので、普通に使ってるだけでそうそうリークしませんし、
仮にリークしたらデバッグモードで通知されることにも変わりないと思うのですが、
new を使う利点として、上記をあげられている根拠がよくわからないです。

επιστημη 2006/01/17(火) 07:20:08
> メモリリークを検出できるという点で、new
> を使用したいと思います。

...わかんね orz
メモリリークを心配しなくて済む方が良いに決まってんじゃん。

RAPT 2006/01/17(火) 11:25:46
ま、malloc() 使うよっか、new の方が、デバッガがリーク個所を教えてくれるから
良いにしても、可能なら、STLの方が安上がりですね。

HI 2006/01/17(火) 23:29:33
わけわかんないですか、どうもすいません。

> メモリリークを検出できるという点で、new
> を使用したいと思います。

strdup 〜 free より new のほうがメモリリーク・・・
という意味です。

> STLでも有益な情報ありがとうございます。
> これからじっくり試してみようと思います。

std::vector<win32::string> args(argv, argv+argc);
のほうがいいんでしょうが、STLもテンプレートも使った
ことがないので、今勉強中です。

わからなければまた質問させていただきますので
よろしくお願いします。

Ban 2006/01/18(水) 00:36:34
> strdup 〜 free より new のほうがメモリリーク・・・
> という意味です。

そういうことならまだわからなくもないですが、
デバッグでメモリリークが検出されるのはどちらでも多分変わらないです。

とはいえ、CならまだしもC++でduplicate使う意味はほぼないですから、
これを気に std::string とか使ってみるのも面白いかと。
# あまりの便利さにC言語には戻れなくなってしまうかもしれませんが(笑

επιστημη 2006/01/20(金) 01:47:06
#include <iostream>
#include <vector>
#include <string>

int main(int argc, char* argv[]) {
  std::vector<std::string> args(argv, argv+argc);
  for ( int i = 0; i < args.size(); ++i ) {
    std::cout << "argv[" << i << "]=" << args[i] << std::endl;
  }
  return 0;
}

…戻れませんねぇ (^^

古いスレッドには追加発言できません。