“sizeof(arr )”会导致未定义的行为吗?
有一个众所周知的模式来计算数组长度:
int arr[10]; size_t len = sizeof(arr) / sizeof(arr[0]); assert(len == 10);
此模式适用于静态数组和常量大小的自动数组。 它也适用于C99中的可变长度数组。
我想应用类似的想法来计算动态数组大小(以字节为单位):
size_t known_len = 10; int *ptr = malloc(known_len * sizeof(int)); size_t size = known_len * sizeof(ptr[0]); assert(size == known_len * sizeof(int));
这比known_len * sizeof(int)
更好,因为sizeof(ptr[0])
不引用实际的数组元素类型。 因此,它不需要代码的读者知道类型。
但是我不清楚表达式sizeof(ptr[0])
会导致不确定的行为。 随着它扩大:
sizeof(ptr[0]) -> sizeof(*((ptr) + (0))) -> sizeof(*ptr)
如果ptr
为0
,结果表达式是有问题的:
sizeof(*((int*) 0))
根据C99标准:
(C99,6.3.2.3p3):“值为
0
的整型常量表达式,或者类型为void *
的表达式,称为空指针常量。” 取消引用空指针是未定义的行为。(C99,6.5.3.2.p4)“如果为指针指定了无效值,则一元
*
运算符的行为未定义.87)”87):“unary
*
运算符解除引用指针的无效值是空指针,地址与指向的对象类型不一致,以及对象在其生命周期结束后的地址。”
但是从未指明这种表达式的sizeof是否会导致未定义的行为。 实际上,应该在编译时评估这样的sizeof。
我的问题是:
- 当
ptr
类型已知并且ptr
的值未知时,可以在代码中使用表达式sizeof(ptr[0])
吗? - 根据C99标准可以certificate这种用途是合理的吗? GNU GCC规范?
表达式ptr[0]
将不会在sizeof(ptr[0])
求值。 大小将通过在编译时使用ptr[0]
的类型来确定。
C11:6.5.3.4:
sizeof
运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称。 大小由操作数的类型确定 。 结果是整数。 如果操作数的类型是可变长度数组类型,则计算操作数; 否则,不评估操作数 ,结果是整数常量。
这意味着,没有未定义的行为。
这不会导致未定义的行为。
除了获取可变长度数组的大小之外, sizeof
是一个编译时常量表达式。 编译器处理表达式以确定其类型,而不生成在编译时计算表达式的代码。 因此, ptr[0]
(它是一个未初始化的指针)的值根本不重要。
而且,如果你想分配十个整数,你应该像这样调用malloc
:
int *ptr = malloc(known_len * sizeof(ptr[0]));
否则,您将分配十个字节,这对于存储十个整数来说太小了。 请注意,在上面的表达式中, ptr
在调用时未初始化,这对于sizeof
来说非常好。
一般情况下,如果我遗漏了某些内容,则在sizeof
下取消引用空指针会导致未定义的行为。 从C99开始, sizeof
不是纯粹的编译时构造。 如果操作数类型是VLA,则在运行时计算sizeof
的操作数。
请考虑以下示例
unsigned n = 10; int (*a)[n] = NULL; // `a` is a pointer to a VLA unsigned i = 0; sizeof a[i++]; // applying `sizeof` to a VLA
根据C99标准,应该评估sizeof
的参数(即i
应该增加)。 然而,我并不完全确定a[0]
中的零点解除引用应该在这里产生未定义的行为。
作为表达式(不是声明), ptr[0]
与*ptr
相同。
只要ptr
的类型已知并且它没有指向void, sizeof
就可以保证在sizeof(*ptr)
和sizeof(ptr[0])
。
代码是邪恶的。
size_t known_len = 10; int *ptr = malloc(known_len); size_t size = known_len * sizeof(ptr[0]); assert(size == known_len * sizeof(int));
从您的角度来看,内存分配的大小为10个字节。 这不是指向int的10个指针。
known_len / sizeof(ptr[0])
将为您提供分配可以容纳的整数数。
因此,您很可能会在分配结束时导致未定义的行为。
考虑malloc(known_length * sizeof ptr [0]);