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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4352768/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:17:42  来源:igfitidea点击:

How do I declare an array of undefined or no initial size?

c

提问by arscariosus

I know it could be done using malloc, but I do not know how to use it yet.

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

For example, I wanted the user to input several numbers using an infinite loop with a sentinel to put a stop into it (i.e. -1), but since I do not know yet how many he/she will input, I have to declare an array with no initial size, but I'm also aware that it won't work like this int arr[]; at compile time since it has to have a definite number of elements.

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

Declaring it with an exaggerated size like int arr[1000];would work but it feels dumb (and waste memory since it would allocate that 1000 integer bytes into the memory) and I would like to know a more elegantway to do this.

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

回答by gnud

This can be done by using a pointer, and allocating memory on the heap using malloc. Note that there is no way to later ask how big that memory block is. You have to keep track of the array size yourself.

这可以通过使用指针来完成,并使用在堆上分配内存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]);
  }
}

That's it. What follows is a more involved explanation of why this works :)

就是这样。下面是一个更复杂的解释为什么它起作用:)

I don't know how well you know C pointers, but array access in C (like array[2]) is actually a shorthand for accessing memory via a pointer. To access the memory pointed to by data, you write *data. This is known as dereferencing the pointer. Since datais of type int *, then *datais of type int. Now to an important piece of information: (data + 2)means "add the byte size of 2 ints to the adress pointed to by data".

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

An array in C is just a sequence of values in adjacent memory. array[1]is just next to array[0]. So when we allocate a big block of memory and want to use it as an array, we need an easy way of getting the direct adress to every element inside. Luckily, C lets us use the array notation on pointers as well. data[0]means the same thing as *(data+0), namely "access the memory pointed to by data". data[2]means *(data+2), and accesses the third intin the memory block.

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

回答by NPE

The way it's often done is as follows:

通常的做法如下:

  • allocate an array of some initial (fairly small) size;
  • read into this array, keeping track of how many elements you've read;
  • once the array is full, reallocate it, doubling the size and preserving (i.e. copying) the contents;
  • repeat until done.
  • 分配一些初始(相当小)大小的数组;
  • 读入这个数组,跟踪你读了多少个元素;
  • 一旦数组已满,重新分配它,将大小加倍并保留(即复制)内容;
  • 重复直到完成。

I find that this pattern comes up pretty frequently.

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

What's interesting about this method is that it allows one to insert Nelements into an empty array one-by-one in amortized O(N)time without knowing Nin advance.

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

回答by Jens Gustedt

Modern C, aka C99, has variable length arrays, VLA. Unfortunately, not all compilers support this but if yours does this would be an alternative.

现代 C,又名 C99,具有可变长度数组VLA。不幸的是,并非所有编译器都支持这一点,但如果您的编译器支持,这将是一个替代方案。

回答by Y.H.

Try to implement dynamic data structure such as a linked list

尝试实现链表等动态数据结构

回答by Michael Burr

Here's a sample program that reads stdininto a memory buffer that grows as needed. It's simple enough that it should give some insight in how you might handle this kind of thing. One thing that's would probably be done differently in a real program is how must the array grows in each allocation - I kept it small here to help keep things simpler if you wanted to step through in a debugger. A real program would probably use a much larger allocation increment (often, the allocation size is doubled, but if you're going to do that you should probably 'cap' the increment at some reasonable size - it might not make sense to double the allocation when you get into the hundreds of megabytes).

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

Also, I used indexed access to the buffer here as an example, but in a real program I probably wouldn't do that.

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

#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);
}

This example combined with examples in other answers should give you an idea of how this kind of thing is handled at a low level.

这个例子结合其他答案中的例子应该会让你了解如何在低级别处理这种事情。

回答by wnoise

malloc()(and its friends free()and realloc()) isthe way to do this in C.

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

回答by khachik

One way I can imagine is to use a linked list to implement such a scenario, if you need all the numbers entered before the user enters something which indicates the loop termination. (posting as the first option, because have never done this for user input, it just seemed to be interesting. Wasteful but artistic)

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

Another way is to do buffered input. Allocate a buffer, fill it, re-allocate, if the loop continues (not elegant, but the most rational for the given use-case).

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

I don't consider the described to be elegant though. Probably, I would change the use-case (the most rational).

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