初始化未知大小的二维数组

时间:2009-09-01 01:31:40

标签: c arrays

我有一个2-D字符数组,例如char aList[numStrings][maxLength]。理想情况下,在程序执行期间,我希望能够修改aList的内容,即添加,修改或删除条目。由于aList可能会发生变化,因此我不想在每次修改aList之后重新编译我的程序。所以我想在程序结束时将aList写入文本文件,然后在下一个程序运行开始时将其读回aList。

但是,我不知道在程序启动时numStrings的值是多少。 (我没有使用C99,所以我不能使用VLA,并从外部文件中获取先前字符串的数量。)当然,我可以将numStrings设置为人为的高值,但是可以格栅!

有没有办法在不知道numStrings值的情况下填充aList?我认为没有(我看过相关的问题),但还有另一种方法可以实现我的需求吗?

6 个答案:

答案 0 :(得分:9)

如果你真的希望能够从网格中间删除项目(你的问题不清楚),你需要某种多重链接结构。这些通常用于实现稀疏数组,因此您可以找到一个预制的。

我正在谈论这样的事情:

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

其中A是对象的根节点,C是列指针数组,R是行指针数组,每个单元指向其行和列的下一个邻居。假定未明确表示的所有单元都具有一些默认值(通常为NULL或0)。

这是一个简单的想法,但是相当挑剔的实现,有很多机会搞砸,所以如果可以,请使用调试库。

答案 1 :(得分:1)

您可以使用动态分配的数组。使用malloc()制作一个,realloc()更改一个的大小,free()完成后使用realloc()。但是another answer已经涵盖了这一点。

另一种选择是使用linked list。这样,每次要扩展数组时都不需要realloc() - {{1}}如果必须将整个数组复制到新位置,则可能相当昂贵。

答案 2 :(得分:0)

您可以动态分配数组:

char **aList;
int i;

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

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

如果您有可能使用C ++而不是C,那么您总是可以使用C ++向量:

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

答案 3 :(得分:0)

您所描述的情况恰恰是malloc的用途 - 分配可变长度的内存块。

答案 4 :(得分:0)

如果你的计划是在阅读文件时填写,你可以做两件事之一。

将字符串数量存储为文件中的第一个元素,然后jgottula的建议就可以正常工作。

或者,你必须使用数组吗?您可以将它们直接读入链表,然后在完成阅读后,将它们移动到一个数组中,然后释放链表。

答案 5 :(得分: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);,整个事情就不复存在了。但是你不能再realloc()因为它不会在内存块的开头插入新的空格(存储aList[])。

这些东西构成了使用C ++向量的非常好的理由;至少C ++会自动进行清理(例如内存异常)。