C语言 如何声明一个未定义或没有初始大小的数组?

iibxawm4  于 2023-10-16  发布在  其他
关注(0)|答案(6)|浏览(140)

我知道这可以用malloc来完成,但我还不知道如何使用它。
例如,我想让用户使用一个带有哨兵的无限循环输入几个数字(即,-1),但由于我还不知道他/她会输入多少,我必须声明一个没有初始大小的数组,但我也知道它不会像这样工作int [];因为它必须有一定数量的元素。
用一个夸张的大小声明它,比如 int [1000]; 会工作,但感觉很笨(浪费内存,因为它会将1000个整数字节分配到内存中),我想知道一个更优雅的方法来做到这一点。

jdzmm42g

jdzmm42g1#

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

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

int main(int argc, char** argv)
{
  /* declare a pointer to 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)的意思是“在data指向的地址上加上2个int的字节大小“。
C中的数组只是相邻内存中的一个值序列。array[1]就在array[0]旁边。因此,当我们分配一大块内存并希望将其用作数组时,我们需要一种简单的方法来获取内部每个元素的直接地址。幸运的是,C也允许我们在指针上使用数组表示法。data[0]*(data+0)的意思相同,即“访问data指向的内存“。data[2]表示*(data+2),访问内存块中的第三个int

wj8zmpe1

wj8zmpe12#

通常的做法如下:

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

我发现这种模式经常出现。
这个方法的有趣之处在于,它允许在不事先知道N的情况下,在分摊O(N)时间内将N元素逐个插入空数组。

w8ntj3qf

w8ntj3qf3#

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

fsi0uk1n

fsi0uk1n4#

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

mpbci0fu

mpbci0fu5#

下面是一个示例程序,它将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);
}

这个例子和其他答案中的例子结合起来,应该会让你给予一个在低层次上如何处理这类事情的想法。

bfnvny8b

bfnvny8b6#

我可以想象的一种方法是使用链表来实现这样的场景,如果你需要在用户输入指示循环终止的东西之前输入所有的数字。(张贴作为第一个选项,因为从来没有这样做的用户输入,它只是似乎很有趣。浪费但艺术
另一种方法是做缓冲输入。分配一个缓冲区,填充它,重新分配,如果循环继续(不优雅,但对于给定的用例最合理)。
我不认为所描述的是优雅的。也许,我会改变用例(最合理的)。

相关问题