具有随机长度的数组的sizeof()

11
你能解释一下 sizeof() 如何与随机长度的数组一起使用吗?我以为对于数组来说,sizeof() 是在编译时计算的,但是,具有随机长度的数组的大小似乎被正确地计算了。
例如:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main(){
    srand ( (unsigned)time ( NULL ) );
    int r = rand()%10;
    int arr[r]; //array with random length
    printf("r = %d size = %d\n",r, sizeof(arr)); //print the random number, array size
    return 0;
}
多次运行后的输出结果:
r = 8 size = 32
r = 6 size = 24
r = 1 size = 4

编译器:gcc 4.4.3


对于 int arr[r]; 这一行代码,我真的不知道它是如何编译通过的。我以为栈中的数组在编译期间必须要知道它们的大小。 - orip
3
请参考C99中的可变长度数组(Variable Length Arrays)。 - Necrolis
1
看这个:https://dev59.com/0nRB5IYBdhLWcg3wCjnO - user576796
4
请注意,如果 rand() % 10 恰好为0,则行为未定义。C语言不支持零大小的数组(尽管gcc可能作为扩展支持)。 - Keith Thompson
4个回答

13
在C99中,变长数组的大小是在运行时计算的。根据C99草案6.5.3.4/2:
“sizeof”运算符返回其操作数的大小(以字节为单位),操作数可以是表达式或类型的括号名称。大小是从操作数的类型确定的。结果是一个整数。如果操作数的类型是变长数组类型,则对操作数进行求值;否则,不对操作数进行求值,结果是一个整数常量。

你能否给我更详细的解释吗?我对这个问题和你的回答都不是很理解。请不要跟我发脾气。谢谢。 - vietean
1
这里的重点是,在C99中,数组的大小不必在编译时指定。在这种情况下,sizeof不是在编译时评估,而是在运行时评估。 - Andreas Brinck
这是否意味着数组的内存占用可能是数组大小加上额外的字节来存储其大小? - zoli2k
@zoli2k:不是的。数组的大小(或更可能是元素数量)将存储在内存中的某个位置,但这不计算为数组大小的一部分。sizeof arr是元素数量乘以每个元素的大小。 - Keith Thompson
大小可能需要存储,也可能不需要,这取决于编译器如何管理堆栈以及是否使用 sizeof。这是一个实现细节。 - R.. GitHub STOP HELPING ICE
1
@R..: 你说得对。大小必须以某种方式可访问,但如果从未使用过,它可以被优化掉,即使它可以在编译时计算(即使它不是常量表达式),也不需要存储在任何地方,即使它被使用。关键是编译器可能会生成额外的数据,可能占用内存,但这些数据不计入sizeof arr的大小。 - Keith Thompson

3
在你的代码中,arr 是一种特殊类型的数组:它是一个 VLA(可变长度数组)。
标准中sizeof的段落(6.5.3.4)说:

如果操作数的类型为可变长度数组类型,则对该操作数进行求值

因此,它不是编译时常量

一个有关语言法律问题的小争论:C标准中的那一部分实际上措辞有些欠佳。它说在表达式 sizeof arr 中,表达式 arr 被求值,但是不清楚这意味着什么。arr 是一个数组类型的表达式;它的值由其元素的值组成。(通常的指针衰减在这个上下文中不会发生。)但是 sizeof arr 当然不需要对数组本身进行评估,它只需要确定它有多大,并且这个信息来自于类型,而不是值。但我不确定该怎样重新表述它。 - Keith Thompson
@Keith,我只是在这里随便想一下,如果我有sizeof(arrparr[++i]),其中arrparr类似于int (*int[6])[t],那么评估表达式的副作用将会增加i,或者如果它是空指针,则会调用未定义的行为(通常是段错误)。我刚试了一下。CLANG在这种情况下确实会增加i,但GCC 4.1.2则不会。CLANG要么没有费心去执行解引用,要么已经被优化掉了(完全合法,因为只有空指针才有意义,而解引用是未定义的行为,因此什么也不做是合法的)。 - Kevin Cathcart
仔细观察后,我发现CLANG确实执行了解引用操作,但它被优化掉了。另外,我应该澄清一下,任何无效指针都是重要的,不仅仅是空指针,但编译器在这种情况下可能仍然不会采取任何措施。 - Kevin Cathcart

2
在C99中,编译器足够智能,知道rand()是在运行时调用的。

LOL,与愚蠢的C89编译器相反 :) - pmg
好吧...并不是rand()使数组特殊。这段代码:int k=42; int arr[k]; sizeof arr;有一个和OP一样特殊的数组。 - pmg
@pmg:我不太明白你的意思。rand() 函数生成一个随机数,然后用于数组大小。 - user195488
2
是的,但 sizeof 运算符“有效”的原因有两个:数组是 VLA,并且编译器符合 C99。请尝试使用 C89 编译器进行此操作:const int k=42; int arr[k]; sizeof arr; - pmg
@Code Monkey:是的。这两种情况共同之处在于,数组长度原则上是在运行时计算的。 - Keith Thompson

2

使用C99引入的VLA(变长数组)时,使用sizeof计算它们的大小是在运行时而不是编译时进行的。

关于这个主题的非常好的参考资料,请点击此处查看。


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