【问题标题】:How do I declare an array of undefined or no initial size?如何声明一个未定义或没有初始大小的数组?
【发布时间】:2011-05-20 03:52:05
【问题描述】:

我知道可以使用malloc,但我还不知道如何使用它。

例如,我希望用户使用带有标记的无限循环输入几个数字以将其停止(即 -1),但由于我还不知道他/她将输入多少,所以我有声明一个没有初始大小的数组,但我也知道它不会像这样 int arr[];在编译时,因为它必须有一定数量的元素。

用像 int arr[1000]; 这样的夸大大小声明它会起作用,但感觉很愚蠢(并且浪费内存,因为它会将 1000 个整数字节分配到内存中),我想知道一种更优雅的方式来做到这一点。

【问题讨论】:

  • malloc 怎么样?数组不是指针,指针也不是数组。但是指针可以像数组一样方便地访问,并且非脚本数组的计算结果是指向第一个元素的指针。
  • @pst:你读过这个问题吗? “我知道它可以使用 malloc 完成,但我还不知道如何使用它。”
  • 我说先生我知道但我还不知道如何使用它,解释如何对我来说是最好的答案:D
  • malloc() 是唯一的方法。 C 本身不支持任意长度的数组。

标签: c


【解决方案1】:

这可以通过使用指针并使用malloc 在堆上分配内存来完成。 请注意,以后无法询问该内存块有多大。您必须自己跟踪数组大小。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv)
{
  /* declare a pointer do an integer */
  int *data; 
  /* we also have to keep track of how big our array is - I use 50 as an example*/
  const int datacount = 50;
  data = malloc(sizeof(int) * datacount); /* allocate memory for 50 int's */
  if (!data) { /* If data == 0 after the call to malloc, allocation failed for some reason */
    perror("Error allocating memory");
    abort();
  }
  /* at this point, we know that data points to a valid block of memory.
     Remember, however, that this memory is not initialized in any way -- it contains garbage.
     Let's start by clearing it. */
  memset(data, 0, sizeof(int)*datacount);
  /* now our array contains all zeroes. */
  data[0] = 1;
  data[2] = 15;
  data[49] = 66; /* the last element in our array, since we start counting from 0 */
  /* Loop through the array, printing out the values (mostly zeroes, but even so) */
  for(int i = 0; i < datacount; ++i) {
    printf("Element %d: %d\n", i, data[i]);
  }
}

就是这样。以下是对为什么会起作用的更复杂的解释:)

我不知道你对 C 指针了解多少,但 C 中的数组访问(如 array[2])实际上是通过指针访问内存的简写。要访问data 指向的内存,您可以编写*data。这称为取消引用指针。因为dataint * 类型,那么*dataint 类型。现在来看一条重要信息:(data + 2) 的意思是“将 2 个整数的字节大小添加到 data 指向的地址”。

C 中的数组只是相邻内存中的一系列值。 array[1] 就在 array[0] 旁边。因此,当我们分配一大块内存并希望将其用作数组时,我们需要一种简单的方法来获取内部每个元素的直接地址。幸运的是,C 也允许我们在指针上使用数组表示法。 data[0]*(data+0)意思一样,即“访问data指向的内存”。 data[2] 表示*(data+2),访问内存块中的第三个int

【讨论】:

  • 谢谢。这是我需要的全面解释,感谢您的额外努力,先生,我现在可以根据您的解释编写代码来尝试一些事情。最后一个问题,假设我想将它扩展到超过 50 个元素,我将如何增加数组大小?
  • 要增加数组大小,你必须使用realloc。这可能复制和移动整个内存块,所以它并不便宜。 linux.die.net/man/3/realloc
【解决方案2】:

常做的方式如下:

  • 分配一些初始(相当小)大小的数组;
  • 读入这个数组,记录你读过多少元素;
  • 一旦数组已满,重新分配它,将大小加倍并保留(即复制)内容;
  • 重复直到完成。

我发现这种模式经常出现。

这种方法的有趣之处在于,它允许在事先不知道N 的情况下,在摊销的O(N) 时间内将N 元素一个接一个地插入到一个空数组中。

【讨论】:

  • 不错的把戏。这似乎是一个简单的,因此也是明智的答案。
【解决方案3】:

现代 C,又名 C99,有 variable length arrays,VLA。不幸的是,并非所有编译器都支持这一点,但如果你的编译器支持,这将是一个替代方案。

【讨论】:

  • 不幸的是,当您超出最初的猜测时,这不会让您调整大小。
  • 谢谢,我刚刚发现了这一点,它很有帮助。为了可移植性,我仍然会尝试找出如何在 C89 中执行此操作。非常感谢。
  • 感谢上帝,不是所有的编译器都支持这个!
【解决方案4】:

尝试实现动态数据结构如linked list

【讨论】:

  • 啊。我无法计算我遇到的 C“老手”的数量,他们似乎没有意识到任何其他类型的动态存储方案甚至是可能的,更不用说可能是最佳的了。似乎人们永远不会从这里继续前进。我更愿意建议他们开始实施更像矢量的东西,正如 aix 所建议的那样 - 如果他们不继续前进,那么在这种情况下损害会小得多。
  • 当然还有更高效的数据结构,事实上,我首先想到的是使用 STL Vector,然后才注意到他实际上是在问 C,链表更像是一个概念而不是要使用的真实数据结构。
【解决方案5】:

这是一个示例程序,它将stdin 读入一个根据需要增长的内存缓冲区。它很简单,应该可以让您深入了解如何处理这种事情。在实际程序中可能会以不同方式完成的一件事是数组必须如何在每个分配中增长 - 如果您想在调试器中单步执行,我将其保持在较小的位置以帮助使事情变得更简单。一个真正的程序可能会使用更大的分配增量(通常,分配大小加倍,但如果你要这样做,你可能应该将增量“限制”在某个合理的大小 - 加倍可能没有意义当你达到数百兆字节时分配)。

另外,我在这里使用了对缓冲区的索引访问作为示例,但在实际程序中我可能不会这样做。

#include <stdlib.h>
#include <stdio.h>


void fatal_error(void);

int main( int argc, char** argv)
{
    int buf_size = 0;
    int buf_used = 0;

    char* buf = NULL;
    char* tmp = NULL;    

    char c;
    int i = 0;

    while ((c = getchar()) != EOF) {
        if (buf_used == buf_size) {
             //need more space in the array

             buf_size += 20;
             tmp = realloc(buf, buf_size); // get a new larger array
             if (!tmp) fatal_error();

             buf = tmp;
        }

        buf[buf_used] = c; // pointer can be indexed like an array
        ++buf_used;
    }

    puts("\n\n*** Dump of stdin ***\n");

    for (i = 0; i < buf_used; ++i) {
        putchar(buf[i]);
    }

    free(buf);

    return 0;
}

void fatal_error(void)
{
    fputs("fatal error - out of memory\n", stderr);
    exit(1);
}

此示例与其他答案中的示例相结合,应该让您了解如何在低级别处理此类事情。

【讨论】:

    【解决方案6】:

    我可以想象的一种方法是使用链表来实现这种情况,如果您需要在用户输入指示循环终止的内容之前输入的所有数字。 (作为第一个选项发布,因为从来没有为用户输入这样做过,它似乎很有趣。浪费但艺术

    另一种方法是缓冲输入。如果循环继续,分配一个缓冲区,填充它,重新分配(不优雅,但对于给定的用例最合理)。

    我不认为描述的优雅。可能,我会改变用例(最合理的)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2021-04-17
      • 2020-10-18
      相关资源
      最近更新 更多