構造体のメンバにCStringを使用する方法

[戻る]
ぴかりん 2006/12/21(木) 16:55:20
はじめまして ぴかりんです

初歩的な質問ですみませんがよろしくお願いします

メンバにCStringを持つ構造体を作成しまして、そのポインタを宣言します

その後にそのポインタの実体をmallocで定義し、値をセットしようとすると

アクセスバイオレーションになってしまいます

CStringのメンバをChar[256]とかに変えてやってみると正常にセットできるのですが

CStringを構造体で使用する場合 なにか注意などあるのでしょうか?

また、下記のように ループで構造体の実体を1つづつ確保していく方法ですが

 構造体のポインタを構造体分++する
 
 構造体のサイズ分 mallocで確保する

この方法で問題なくできますでしょうか?

大変初歩的な質問で申し訳ありませんが よろしくお願いいたします。



typedef struct{
    CString AAA;
    CString BBB;
}CCC;

--------------------------------
void ddd()
{
   ccc* pccc;


   while(1)
   {
     pccc = (CCC*)malloc(sizeof(CCC));

     pccc->AAA = "1234";  ← ここでおちる

     pccc++;
   
   }

}

Blue 2006/12/21(木) 17:02:11
クラスを含む構造体(非PODな構造体)をmallocで使うことは出来ないでしょう。
newを使えば、CStringのコンストラクタも動きます。
newをつかないのであれば、CStringをメンバにするのは不可能です。
char*にして_strdupとかを使ってください。

typedef struct{
    char* AAA;
    char* BBB;
}CCC;

     pccc = (CCC*)malloc(sizeof(CCC));
     pccc->AAA = _strdup("1234");


当然 free は pccc と pccc->AAA(pccc->BBBも)に対してする。

ぴかりん 2006/12/21(木) 17:42:30
Blueさん お返事ありがとうございます


今後のためにもどうしてもCStringを使いたいので下記のようにやってみましたが、

やはりアクセスバイオレーションが発生します

これはnewの使い方がおかしいのでしょうか?

やりたいことは CCCの構造体の領域を可変でデータ分だけ確保する

ということです。

ご教授のほどよろしくお願いいたします。



typedef struct{
    CString AAA;
    CString BBB;
}CCC;

--------------------------------
void ddd()
{
   ccc* pccc;


   while(1)
   {
     pccc = new CCC;

     pccc->AAA = "1234";  ← ここでおちる

     pccc++;
   
   }

}

Blue 2006/12/21(木) 17:47:55
>while(1)
無限ループ?

> pccc++;
これなんでしょうか?

επιστημη [HomePage] 2006/12/21(木) 17:49:20
無限ループでnewを繰り返せばそりゃいつかは落ちるべなぁ。

ぴかりん 2006/12/21(木) 17:54:32
while(1)の無限ループは例で
実際はデータ数分ループさせようと思ってます

pccc++は
それで CCC型のポインタ pcccをCCCサイズ分メモリ領域をずらして
次のデータの領域を確保しようとしています。

newを使用する場合はこんな使い方はしないんでしょうか?

たいちう 2006/12/21(木) 17:55:06
あと、ccc* pccc; このcccはCCCの間違いでは?

επιστημη [HomePage] 2006/12/21(木) 18:05:45
N個分の連続領域が欲しいなら

CCC* p = new CCC[N];
for ( int i = 0; i < N; ++i ) {
  p[i].AAA = "1234";
}
...
delete[] p;

ですが。

ぴかりん 2006/12/21(木) 18:14:30
お返事ありがとうございます

たいちうさん > その通りです CCCの間違いでした

N個分の領域がほしいのはほしいんですが、そのN個ってのが可変で

毎回数が変わります

なのでファイルからデータを取得し、領域を確保して保存

というのを繰り返してデータ分だけ領域を確保したいと思ってます

newはそういう使用方法はありませんでしょうか?

省略〜

CCC *p;

while( fgets(cWork,10,fp) != NULL )
{
  p = new CCC;
  
  p.AAA = cWork;

  p++;
}

こんなイメージです

もしよい方法がありましたらご教授願います

よろしくお願いします

シャノン 2006/12/21(木) 18:44:17
> CCC* p = new CCC[N];

この N を変数にすればよろしい。

シャノン 2006/12/21(木) 18:45:44
> while( fgets(cWork,10,fp) != NULL )
> {
>   p = new CCC;
>   p.AAA = cWork;
>   p++;
> }

ちょっと待てどんなイメージだそれ。
1行読むごとに CCC を何個確保したいかが変わるの?

Blue 2006/12/21(木) 19:49:34
動的配列使ったほうがよさげ。
そうすればポインタなんて関係ないかと。

#include <afxtempl.h>


CArray<CCC, CCC> ary;
CCC c;

while ( XXXX )
{
    c.AAA = "xxxxx";
    ary.Add( c );
}

チョーヤ 2006/12/22(金) 11:45:06
pccc->AAA = "1234";  ← ここでおちる

-----------------------------
2回目のループで落ちます。

なぜなら、pccc++;この処理で、領域を確保していないところにpcccが移動するから。

pcccはCCC構造体をいれるポインター領域です。
この領域は最初の宣言CCC* pccc;で確保しています。
なので、『pccc = new CCC』で確保したポインターを代入するのはOK。
でも、pccc++;でpcccが領域確保していない所に移動するので、
この領域に『pccc = new CCC』するのは駄目。
(既にこの時点でおかしな事になってるが、なんとかがんばって動き)
pccc->AAA = "1234";  ← ここで力尽きる。。。

といった感じかと・・・・

επιστημη [HomePage] 2006/12/22(金) 11:50:15
> なぜなら、pccc++;この処理で、領域を確保していないところにpcccが移動するから。

ホントか?
その後 pccc = new CCC; によってpcccを書き換えてるんだから、
「pccc++ する意味がなくなる」だけじゃね?

チョーヤ 2006/12/22(金) 11:53:40
補足。

CCCがwhileの中だけしか使わないのであれば下でいけます。

-----------------------------------------------------------
CCC *p;

while( fgets(cWork,10,fp) != NULL )
{
  p = new CCC;
  
  p.AAA = cWork;

  delete p;
}
-----------------------------------------------------------

チョーヤ 2006/12/22(金) 11:55:43
επιστημηさん。

>「pccc++ する意味がなくなる」だけじゃね?
おお、その通りです。

ぴかりんさん、すみませんうそ書きました。
私の発言は無視してください。m(_"_)m

PATIO 2006/12/22(金) 13:25:08
うーん、C++言語の勉強をちゃんとした方が良いのではと言う気が・・・。

newを使わないとコンスタラクタが動かないとかこの辺は入門書にも
書いてある話なので、一足飛びに使おうとしないで基礎を一通り勉強
されたほうが良いと思いますよ。

あと、ステップ・バイ・ステップで進めていくつもりなら
ソースを書くときもそれを考慮して書かないとへんな所ではまります。
今回の例から行くと複数の配列確保をどうするのかまで
考えきれて無いのにループにしてしまっている時点で既に問題かと。

ぴかりん 2006/12/22(金) 14:30:59
[[解決]]
みなさん 色々なご指摘ありがとうございます

もう一度基礎から見直してみることにします

ありがとうございました

bbbb 2007/02/14(水) 12:49:17
これってこういうことじゃ?


typedef struct{
    CString AAA;
    CString BBB;
}CCC;

--------------------------------
void ddd()
{
   ccc* pccc;


   while(1)
   {
     pccc = (CCC*)malloc(sizeof(CCC));

     pccc->AAA = new CString();
     pccc->AAA = "1234"
     pccc++;
   
   }
   追加終わったら
   delete pccc->AAA 
   free(pccc );

}

Blue 2007/02/14(水) 13:13:02
>これってこういうことじゃ?
>pccc->AAA = new CString();
AAA は CString*型でないですよ。

Ban 2007/02/14(水) 13:57:02
仮に、AAAがCString*型でも、今度はこっちがバグってますし。
     pccc->AAA = "1234"
ポインタの参照先を直接書き換えてるだけで、CString::operator =は動かない。

# 配置newとか駆使してごにょごにょとか考えるよりは、
# 素直にstd::vectorやC〜Arrayの類を使うことを考えるという方向でよいと思います。

Ban 2007/02/14(水) 14:09:01
> ポインタの参照先を直接書き換えてるだけで
# 書き換えも無理やりキャストが必要。

bbbb 2007/02/14(水) 14:47:21
失礼。ではこれではだめですか?

typedef struct{
    CString* AAA;
    CString* BBB;
}CCC;

--------------------------------
void ddd()
{
   ccc* pccc;


   while(1)
   {
     pccc = (CCC*)malloc(sizeof(CCC));

     pccc->AAA = new CString();
     *pccc->AAA = "1234"
     pccc++;
   
   }
   追加終わったら
   delete pccc->AAA 
   free(pccc );

?

Blue 2007/02/14(水) 14:52:48
これならばOKですが、
元からCStringでもったCCCをmallocせず、newすればいいだけなのであまり意味ないので
は?
(いちいちmallocしてnewする意味あるのかなぁと)

Ban 2007/02/14(水) 15:02:59
ループの中でpcccを更新してしまったら、ループの後にいくら解放処理を書いても、
   delete pccc->AAA 
   free(pccc );
最後のpcccしか解放できず他は全部リークすると思いますが。


> 動的配列使ったほうがよさげ。
> そうすればポインタなんて関係ないかと。

PODが必要ならCStringは諦めるしかないし、
Blueさんの提案どおりにこうにしない理由がよくわからず。

# 配置newってのは最後の手段だと思う…。

bbbb 2007/02/14(水) 15:13:39
最終的にはこんな感じで使いたいわけではなく?

typedef struct{
    CString*    StrAdress;
    CString*    StrParent;
    CString*    StrName;
    HTREEITEM    item;
    BOOL    bRet;
}S_VIEW_DATA;

typedef struct{
    S_VIEW_DATA*    pdata;
    long        count;
}S_VIEW;
//***********************************************************************
//
//
//
//***********************************************************************
void CTabDlg::InPutFile(CString StrFileName)
{
    //変数定義
    S_VIEW    s_view;
    CString StrLine;
    CString    StrTemp;
    long i,j;
    long pos;
    long setCount;
    CStdioFile file;
    CString    StrAdress;
    CString    StrParent;
    CString    StrName;



    //初期化処理
    s_view.count = 0;
    setCount = 0;

    //----------------------------------
    // 表示項目全削除
    //----------------------------------
    m_tree.DeleteAllItems();



    //----------------------------------
    // ファイル読み出し
    //----------------------------------
    file.Open(StrFileName,CFile::modeRead);

    //----------------------------------
    // 項目数取得
    //----------------------------------
    while(1)
    {
        file.ReadString(StrLine);
        if(StrLine != "")
        {
            s_view.count++;
        }
        else
        {
            break;
        }
    }
    file.Seek(0,0);

    //----------------------------------
    // データ領域取得
    //----------------------------------
    s_view.pdata = (S_VIEW_DATA*)malloc(sizeof(S_VIEW_DATA) * 
s_view.count);


    //----------------------------------
    // データの読み取り
    //----------------------------------
    for(i = 0; i < s_view.count; i++)
    {
        s_view.pdata[i].StrAdress = new CString();
        s_view.pdata[i].StrParent = new CString();
        s_view.pdata[i].StrName   = new CString();
        s_view.pdata[i].bRet      = FALSE;
        file.ReadString(StrLine);

        //1つ目
        pos   = StrLine.Find(",",0);
        StrAdress = StrLine.Left(pos);
        *s_view.pdata[i].StrAdress = StrAdress;
        StrLine.Delete(0,pos+1);
    

        //2つ目
        pos   = StrLine.Find(",",0); 
        StrParent = StrLine.Left(pos);
        *s_view.pdata[i].StrParent = StrParent;
        StrLine.Delete(0,pos+1);

        //3つ目
        StrName = StrLine.Left(StrLine.GetLength());
        *s_view.pdata[i].StrName = StrName;
    }

    //----------------------------------
    // 先頭ノードの設定
    //----------------------------------
    for(i = 0; i < s_view.count; i++)
    {
        if(*s_view.pdata[i].StrParent == "0x00000000")
        {
            s_view.pdata[i].item = m_tree.InsertItem
(*s_view.pdata[i].StrName,TVI_ROOT);
            setCount++;
        }
    }
    if(setCount != s_view.count)
    {
        while(1)
        {
            //--------------------------
            // 子供ノードの設定
            //--------------------------
            for(i = 0; i < s_view.count; i++)
            {
                for(j = 0; j < s_view.count; j++)
                {
                    if(*s_view.pdata[j].StrParent == 
*s_view.pdata[i].StrAdress)
                    {
                        s_view.pdata[j].item = 
m_tree.InsertItem(*s_view.pdata[j].StrName,s_view.pdata[i].item);
                        setCount++;
                        if(setCount == 
s_view.count)
                        {
                            break;
                        }
                    }
                }
            }
            if(setCount == s_view.count)
            {
                break;
            }
        }
    }
    //----------------------------------
    // 領域開放
    //----------------------------------
    for(i = 0; i < s_view.count; i++)
    {
        delete s_view.pdata[i].StrAdress;
        delete s_view.pdata[i].StrParent;
        delete s_view.pdata[i].StrName;
    }
    free(s_view.pdata);
    file.Close();


}

Blue 2007/02/14(水) 17:15:59
ですから、元からnewすれば構造体のメンバをポインタにする必要もないのですが。

struct S_VIEW_DATA
{
    CString    StrAdress;
    CString    StrParent;
    CString    StrName;
    HTREEITEM  item;
    BOOL       bRet;
};

    //----------------------------------
    // データ領域取得
    //----------------------------------
    s_view.pdata = new S_VIEW_DATA[s_view.count];



    //----------------------------------
    // 領域開放
    //----------------------------------
    delete [] s_view.pdata;


んでもって、MFCならCArray<S_VIEW_DATA, S_VIEW_DATA>を使うと便利。
(これなら前もってサイズを求めるために2度ファイルを読む必要がない)

bbbb 2007/02/14(水) 17:39:14
そうなんですか?
それは知りませんでした。
ありがとうございます。

menba- 2007/02/14(水) 18:07:05
そんな使い方があったとは、知りませんでした。

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