如何声明未定义或没有初始大小的数组?

时间:2010-12-04 09:05:14

标签: c

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

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

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

7 个答案:

答案 0 :(得分:34)

这可以通过使用指针,并使用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。这称为解除引用指针。由于data的类型为int *,因此*data的类型为int。现在转到一条重要的信息:(data + 2)表示“将{1}的字节大小添加到data指向的地址”。

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

答案 1 :(得分:13)

经常这样做的方式如下:

  • 分配一些初始(相当小)的数组;
  • 读入此数组,跟踪您已阅读的元素数量;
  • 一旦数组已满,重新分配,大小加倍并保留(即复制)内容;
  • 重复完成。

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

这种方法的有趣之处在于它允许人们在摊销N时逐个将O(N)个元素插入空数组中而不事先知道N

答案 2 :(得分:5)

Modern C,又名C99,有variable length arrays,VLA。不幸的是,并非所有编译器都支持这一点,但如果你的确支持这一点,那将是另一种选择。

答案 3 :(得分:2)

尝试实施动态数据结构,例如linked list

答案 4 :(得分:1)

malloc()(及其朋友free()realloc()在C中执行此操作的方式。

答案 5 :(得分:1)

这是一个示例程序,它将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 :(得分:0)

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

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

我不认为所描述的是优雅的。也许,我会改变用例(最理性的)。