2009-09-01 19 views
1

私は2次元配列の文字を持っています。 char aList[numStrings][maxLength]。理想的には、プログラムの実行中に、aListの内容を変更する、つまりエントリを追加、修正、または削除できるようにしたいと考えています。 aListは変更されることがあるので、aListを変更するたびにプログラムを再コンパイルする必要はありません。ですから、プログラムの最後にaListをテキストファイルに書き出し、次のプログラムの実行開始時にaListに読み戻したいと思います。未知のサイズの2次元配列を初期化する

しかし、私はプログラム開始時にnumStringsの値がわかりません。 (私はC99を使用していないので、VLAを使用することはできず、外部ファイルから以前の文字列の数を拾います)もちろん、numStringsを人為的に高い値に設定することはできますが、

numStringsの値を知らなくてもaListに値を設定する方法はありますか?私はそこに(私は関連する質問を見た)とは思わないが、私が必要とするものを達成する別の方法があるのだろうか?

std::vector<std::vector<char> > aList; 
+0

これが基本的な質問である場合、既にサイト上にあるc(または時にはC++)の多次元動的配列に関する多くの質問があります。この検索を試してください:http://stackoverflow.com/search?q=[c]+multi+dimension+array。 – dmckee

答えて

0

あなたは、動的配列を割り当てることができます記述されているのは、正確にはmallocのためのものです - 可変長ブロックのメモリを割り当てる。

+0

@jgottula:私が言ったように、私はnumStringsの値を知らない。そして私はCを使用しています。 – paoloricardo

+0

ああ、持っています。 numStringsは変数にすることができますが、reallocを使って配列のサイズを変更することもできます。 – jgottula

+0

普通の人のように 'std :: string'のベクトルの代わりに' char'のベクトルのベクトルをC++で使うのはなぜですか? –

0

状況ます:万が一、あなたがC++ではなくCを使用することができ、場合

char **aList; 
int i; 

aList = malloc(sizeof(char *) * numStrings); 

for (i = 0; i < numStrings; i++) 
{ 
    aList[i] = malloc(maxLength); 
} 

すると、あなたは常にC++のベクターを使用することができます。

+0

@mark:@jgottulaへの私のコメントを参照してください。 – paoloricardo

+0

@mark:申し訳ありませんが、私は外部ファイルからnumStringsの値を読み取ることができます。 – paoloricardo

+0

番号をファイルに保存することができますファイルを2回渡すこともできます。配列のサイズを取得するために文字列の数を数え、その後実際に文字列を格納します。 –

0

ファイルの読み込み中に計画を立てる場合は、次の2つのうちのいずれかを実行できます。

ファイルの最初の要素として文字列の数を格納すると、jgottulaによる提案はうまくいくはずです。

または、配列を使用する必要がありますか?それらをリンクリストに直接読み込んだり、読み終わったら配列に移動して、リンクされたリストを解放することができます。

+0

@James:私はnumStringsの値を外部ファイルに格納することを考えていましたが、malloc()は考えていませんでした。リンクされたリストの考え方も便利です。 – paoloricardo

+0

'numStrings'の値を外部ファイルに格納することやそれをどこにでも保存することにはいくつか問題があると思いますが、それは私の設計上の決定ではありません。しかし、もしあれば、私は間違いなくリンクリストに行きます。実装がとても簡単で、さまざまなメリットがあります。 –

1

動的に割り当てられた配列を使用できます。 malloc()を使用して1つを作成し、realloc()は1のサイズを変更し、free()は変更します。しかしこれは既にanother answerでカバーされています。

もう1つの方法は、linked listを使用することです。アレイ全体を新しい位置にコピーする必要がある場合は、realloc()realloc()にする必要はありません。realloc()はかなり高価になります。

+0

@Chris:ありがとう、@Jamesへの私の返信を参照してください – paoloricardo

9

本当にグリッドの中央からアイテムを削除したい場合(あなたの質問はこれではっきりしません)、何らかの種類の多重リンク構造が必要です。これらはしばしば疎配列を実装するために使用されるため、あらかじめ作成された配列を見つけることができます。私はこのような何かについて話している

:Aは、オブジェクトのルートノードである

+---+ 
| A | 
+-|\+ 
    | \ 
    | \ 
    | \ 
    | \ 
    |  +----+----+----+ 
    |  | C0 | C1 | C2 | ... 
    |  +--|-+----+--|-+ 
    |  |   | 
    |  |   | 
+-V--+ +--V-+  | +----+ 
| R0 |->|a0,0|-------+>|a0,3|--> ... 
+----+ +--|-+ +--V-+----+ 
| R1 |-----+----->|a1,2|--> ... 
+----+  |  +--|-+ 
...  V   | 
      ...  V 
        ... 

、Cは列のポインタの配列であり、Rは、行ポインタの配列であり、各セル点行と列の両方に沿って隣の隣に移動します。明示的に表されていないすべてのセルは、デフォルト値(通常はNULLまたは0)を持つとみなされます。

これは簡単な考えですが、非常にうまく実装されています。混乱する可能性が非常に高いので、可能であればデバッグされたライブラリを使用してください。

+2

+1のASCII gfx :) - それを作成するツールを使用しましたか? – waqasahmed

+1

ツールなし、ちょっとした忍耐。 – dmckee

+0

6年後、忍耐は今でも最高のツールです。 – paulotorrens

0

2D Cスタイルの配列は一般的に残念ですが、紙の上でシンプルで便利ですが、動的メモリ管理の実装 - 割り当ての失敗やクリーンアップ/サイズ変更の処理 - はしばしば詳細が難しい。これらのアプローチの大きな問題は、クリーンアップが厄介であることは通常あり

/* 
* Start with an array that can hold INITIAL_NUM elements of (char*). 
*/ 
char **aList = (char**)malloc(INITIAL_NUM, sizeof(*aList)); 
int curIdx = 0, curListSz = INITIAL_NUM; 

while (more_stuff_to_append) { 
    /* 
    * Still space in the existing list ? If not - resize 
    */ 
    if (curIdx >= INITIAL_NUM) { 
     curListSz += ALLOC_INCREMENT_FOR_ALIST; 
     if ((aList = realloc(aList, curListSz * sizeof(*aList))) == NULL) 
      error_and_yucky_cleanup("can't resize list, out of memory"); 
    } 

    /* 
    * Allocate a new element. 
    * Note that if it's _known_ in advance that all elements 
    * are the same size, then malloc'ing a big block and slicing 
    * that into pieces is more efficient. 
    */ 
    if ((aList[curIdx] = malloc(new_elem_size, sizeof(char)) == NULL) 
     error_and_yucky_cleanup("out of memory"); 

    /* 
    * put the contents into the new buffer, however that's done. 
    */ 
    populate_new_entry(aList[curIdx]); 
    curIdx++; 
} 

:あなたは何ができるか

のようなものです。配列内を移動してすべての要素に対してfree()を呼び出し、最後にaListをクリーンアップする必要があります。

すべてのサイズを事前に知っていれば、のメモリブロックを割り当てて、aListとすべての要素を保持することができます。このようなものを経由して動作します:

#define LISTSZ(lst) (NUMSTRINGS_MAX * sizeof(*(lst))) 
#define ELEMSZ(lst) (STRINGSIZE_MAX * sizeof(**(lst))) 

char **aList = malloc(LISTSZ(aList) + NUMSTRINGS * ELEMSZ(aList)); 
char *curElem = ((char*)aList) + LISTSZ(aList)); 
int i; 

for (i = 0; i < NUMSTRINGS_MAX; i++) { 
    aList[i] = curElem; 
    curElem += ELEMSZ(aList); 
} 

この利点は、クリーンアップが些細なことである - ちょうどfree((char*)aList);を呼び出し、全体のことを行っています。しかし、メモリブロックの先頭に新しいスペースを挿入しないようにすると(aList[]が格納されているため)、realloc()をもう使用できなくなります。

これらのことは、C++ベクタを使用するための本当の理由を構成します。少なくともC++は自動的にクリーンアップ(メモリ不足例外など)を自動的に行います。

関連する問題