可以" sizeof(arr [0])"导致未定义的行为?

时间:2015-06-10 19:46:26

标签: c arrays sizeof c99 undefined-behavior

有一种众所周知的计算阵列长度的模式:

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) 

如果ptr0

,则结果表达式存在疑问
sizeof(*((int*) 0)) 

根据C99标准:

  

(C99,6.3.2.3p3):"一个整数常量表达式,其值为0,   或者这种表达式转换为类型void *,称为空指针   。恒定"取消引用空指针是未定义的行为。

     

(C99,6.5.3.2.p4)"如果已为此分配了无效值   指针,一元*运算符的行为是undefined.87)"

     

87):"用于取消引用指针的无效值   一元*运算符是空指针,地址不合适   对齐指向的对象类型和一个地址   对象在其生命周期结束后。"

但它从未指明此类表达式的sizeof是否会导致未定义的行为。实际上,应该在编译时评估这样的sizeof。

我的问题是:

  • sizeof(ptr[0])的类型已知并且ptr的值未知时,可以在代码中使用表达式ptr吗?
  • 根据C99标准可以证明这种用途是合理的吗? GNU GCC规范?

5 个答案:

答案 0 :(得分:17)

ptr[0]中不会评估表达式sizeof(ptr[0])。大小将通过在编译时使用ptr[0]的类型来确定。

C11:6.5.3.4:

  

sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称。 大小取决于操作数的类型。结果是整数。 如果操作数的类型是可变长度数组类型,则计算操作数;否则,不评估操作数,结果是整数常量。

这意味着,没有未定义的行为。

答案 1 :(得分:11)

这不会导致未定义的行为。

除了获取可变长度数组的大小之外,sizeof是一个编译时常量表达式。编译器处理表达式以确定其类型,而不生成在编译时评估表达式的代码。因此,ptr[0](未初始化指针)的值根本不重要。

此外,如果你想分配10个整数,你应该像这样调用malloc

int *ptr = malloc(known_len * sizeof(ptr[0])); 

否则,您将分配十个字节,这对于存储十个整数来说太小了。请注意,在上面的表达式中,ptr在调用时未初始化,这对于sizeof来说非常好。

答案 2 :(得分:2)

一般情况下,如果我遗漏了某些内容,则取消引用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]中的零点解除引用应该在这里产生未定义的行为。

答案 3 :(得分:1)

作为表达式(不是声明),ptr[0]*ptr相同。

只要ptr的类型已知并且未指向无效,sizeof就可以保证sizeof(*ptr)以及sizeof(ptr[0])

答案 4 :(得分:1)

代码是邪恶的。

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]);