【问题标题】:Initialize 2-D array of unknown size初始化未知大小的二维数组
【发布时间】:2009-09-01 01:31:40
【问题描述】:

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

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

有没有办法在不知道 numStrings 值的情况下填充 aList?我认为没有(我已经查看了相关问题),但还有其他方法可以实现我的需要吗?

【问题讨论】:

标签: c arrays


【解决方案1】:

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

我说的是这样的事情:

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

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

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

【讨论】:

  • 六年后,耐心仍然是最好的工具。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    您所描述的情况正是malloc 的用途——分配一个可变长度的内存块。

    【讨论】:

    • @mark: 抱歉,我发现我可以从外部文件中读取 numStrings 的值
    • 您可以将数字存储在文件中(即作为文件中的第一行),或者您可以对文件进行两次传递 - 计算字符串的数量以获得您的数组大小,然后实际上存储字符串。
    • @Mark:我之前使用单独的外部文件来保存控制信息,效果很好
    • 我对在文件中存储行数的想法在 cmets 到另一个答案这里,我不会重复它们,但在我看来,两遍解决方案要好得多,即使您一定会遇到巨大的性能冲击。但是,您可以通过使用链表来消除性能损失和先存储值的问题。
    • @Chris:我注意到您关于使用外部文件的观点。我将研究链表的想法。
    【解决方案4】:

    如果您的计划是在读取文件时进行填充,您可以做以下两件事之一。

    将字符串的数量存储为文件中的第一个元素,然后 jgottula 的建议会很好。

    或者,你必须使用数组吗?可以直接将它们读入链表,读完后将它们移入数组,释放链表。

    【讨论】:

    • @James:我曾想过将 numStrings 的值存储在外部文件中,但没有想到 malloc()。链表的想法也很有用。
    • 我认为将numStrings 的值存储在外部文件中或将其存储在任何地方都存在很多问题,但这不是我的设计决定。但是,如果是这样,我肯定会选择链表。它非常容易实现,并为您带来各种好处。
    【解决方案5】:

    一般来说,二维 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++ 会自动进行清理(例如内存不足异常)。

    【讨论】:

      【解决方案6】:

      可以动态分配数组:

      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;
      

      【讨论】:

      • @jgottula:正如我所说,我不知道 numStrings 的值。我正在使用 C。
      • 啊,明白了。 numStrings 可以是一个变量,但你也可以使用 realloc 来调整数组的大小。
      • 为什么要在 C++ 中使用 char 的向量,而不是像普通人那样使用 std::string 的向量?
      • @jgottula:但我可以从外部文件中读取 numStrings 的值(正如我最初暗示的那样)
      • 在我看来,将 numStrings 存储为文件的第一个值是一个半途而废的解决方案。某些涂料不可避免地会通过添加三行(或更糟糕的是,删除三行)并忘记更改开头的条目数来破坏您的程序,然后您在哪里?
      猜你喜欢
      • 1970-01-01
      • 2023-04-11
      • 2020-10-11
      • 2015-03-03
      • 1970-01-01
      • 2018-03-04
      • 1970-01-01
      • 1970-01-01
      • 2014-10-31
      相关资源
      最近更新 更多