如何在C语言中创建一个不需要声明大小的数组?

17

我正在尝试创建一个没有大小限制的整型和浮点型数组(它可能为0,也可能会在用户使用程序时增加大小)。

我曾尝试执行以下操作:

int bills[];

float totalAmount[];

我不能指定一个最大大小,因为我正在使用for循环打印每个数组(如果我指定了99的大小,我将打印99行,而我不想要那样)。


1
如果您不知道需要多少存储空间,那么您需要动态分配内存。请查阅malloc()free()文档。 - Tanveer Badar
2
你在C语言中无法这样做,但是有许多替代方案,但我认为我们不能比一本好的C语言书籍更好地帮助你。作为初学者,可以查看malloc()realloc()来寻找简单的解决方案。 - Stargateur
如果最大大小已知(且较小),则可以使用 int bills[99];,然后单独跟踪已使用项目的数量。无论如何,您都需要跟踪钞票的数量。 - Bo Persson
4个回答

30

C不支持具有动态元素数量的数组。数组的元素数量必须在编译时确定,或者自C99以来可以在创建时运行时计算确定。一旦数组被创建,其大小就是固定的,无法更改。在数组定义或数组声明中,有几种情况下大小没有在[]之间明确指定。

如果提供了初始化程序,则可以在左侧维度上没有明确大小的情况下定义数组。编译器将从初始化程序推断出其大小:

int a[] = { 1, 2, 3 };              // equivalent to int a[3] = { 1, 2, 3 };
int m[][2] = {{ 1, 2 }, { 3, 4 }};  // equivalent to int m[2][2] = {{ 1, 2 }, { 3, 4 }};
char s[] = "Hello world\n";         // equivalent to char s[13] = "Hello world\n";

请注意编译器在字符串情况下添加隐式的空终止符。

在多种情况下,您可以声明左侧维度没有大小指示符的数组:

  • 作为具有 extern 类存储的全局变量(数组在其他地方定义),
  • 作为函数参数:int main(int argc, char *argv[])。在这种情况下,左侧维度的大小规定将被忽略。
  • 作为具有多个命名成员的 struct 的最后一个成员。这是C99的一种扩展,称为柔性数组

编译器没有有关这些数组实际大小的信息。程序员将使用其他信息确定长度,例如从单独的变量或数组内容中获取长度。

对于函数参数,数组将作为指针传递,即使指定了元素数量,sizeof(argv)也将计算为指针大小。


你知道在C++中,是否可以声明一个没有左侧维度大小限定符的数组,这种情况与C语言相同吗?(显然,“struct”可能需要更改为“struct”或“class”)。我尝试了函数参数的情况;语法似乎是有效的,但变量变成了指针(根据typeid(the_variable).name)。 - HelloGoodbye
此外,可以提到C99灵活数组仅允许在至少有两个命名成员的结构体中使用。 - HelloGoodbye
@HelloGoodbye:提出了一个很好的观点,关于多个命名成员的问题...回答已经修改。 - chqrlie
@HelloGoodbye:数组总是作为指向它们第一个元素的指针传递。 - chqrlie
在C语言中,数组总是作为指向它们的第一个元素的指针传递,但在C++中不是这样吧?我相信在C++中,带有大小说明符的数组是它自己的数据类型。或者你是指当你将其用作函数参数时,数据类型会变成指针? - HelloGoodbye

10

您不能声明一个没有大小的数组,而是应该声明一个指向若干个记录的指针。

因此,如果您想这样做:

int bills[];

在C中正确的做法是:
int* bills;

你将需要在某个时刻分配大小并初始化数组。

bills = (int*)malloc(sizeof(int)*items);

对于其他数据类型的数组也是如此。如果直到运行时才知道数组的大小,则应使用指向在运行时分配给正确大小的内存的指针。


7
我需要为您翻译的内容是:“我需不需要对 malloc 的返回值进行类型转换?” 答案是:不需要。在 C 语言中,malloc 函数返回一个 void* 类型的指针,可以直接赋值给其他指针类型变量使用,无需进行强制类型转换。 - Stargateur
@EdwinBuck 我不是母语为英语的人,所以我没有完全理解你的评论。 如果你觉得我对你进行了个人攻击,那我很抱歉。 我认为关于“旧编译器可能”的任何争论都是无意义的,因为我们已经处于2018年。 如果你把错误类型的指针强行转换,那么你低估了这个问题。 我认为任何现代C代码都应该使用bills = malloc(sizeof * bills * items);,而那些老程序员则应该放弃他们的旧习惯。 现代C希望不再依赖于旧的C。 这是30年来C程序员的经验得出结论,最好以这种方式写它。 - Stargateur

6
你可以使用 malloc()(或calloc())、realloc()free() 的组合来实现这一点。
可以将内存分配为固定大小的块,而不是为每个要存储的数字重新分配内存。
让我们定义一个宏(或者你喜欢的constBLOCK_SIZE
#define BLOCK_SIZE 10

首先声明一个适当类型的指针并分配第一个块。

请注意,malloc()realloc() 在出现一些错误,如内存不足等原因时会返回NULL

int *ptr=malloc(sizeof(int)*BLOCK_SIZE);    
if(ptr==NULL)
{
    perror("some error");
    return 1;
}

现在声明一个变量来存储根据当前分配的内存可以达到的最大索引(以避免非法内存访问)。
int max_index = BLOCK_SIZE-1;

现在使用一个循环。
for(int i=0; ; ++i)
{
    if(i > max_index)
    {
        ptr=realloc(ptr, (max_index+1 + BLOCK_SIZE)*sizeof(int));
        if(ptr == NULL)
        {
            perror("insufficient memory!");
            break;
        }
        printf("\nRealloced!");
        max_index += BLOCK_SIZE;
    }
    scanf("%d", &ptr[i]);
    printf("\n%d: %d", i, ptr[i]);
}

在每次迭代中,我们会检查i是否大于max_index。如果是,就会使用realloc()分配另一个内存块,并读取值。
完成对内存的使用后,请不要忘记释放它。
free(ptr);

此外,正如在帖子中讨论的那样,malloc()实际上与realloc()相同,只是后者的第一个参数为NULL
在您发布的代码中,没有必要显式地将calloc()的返回值转换为目标指针类型,因为返回的是一个void指针,它会自动转换。
请参见

0

我认为你可以给它一个最大尺寸,如果你只想显示前几个元素,你可以使用一个for循环仅限于该元素,对于输入也是如此,如果你想初始化前30个元素,就用一个for循环到30。


目前你的回答不够清晰。请编辑并添加更多细节,以帮助其他人理解它如何回答所提出的问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接