有一种众所周知的计算阵列长度的模式:
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)"如果已为此分配了无效值 指针,一元
*
运算符的行为是undefined.87)"87):"用于取消引用指针的无效值 一元
*
运算符是空指针,地址不合适 对齐指向的对象类型和一个地址 对象在其生命周期结束后。"
但它从未指明此类表达式的sizeof是否会导致未定义的行为。实际上,应该在编译时评估这样的sizeof。
我的问题是:
sizeof(ptr[0])
的类型已知并且ptr
的值未知时,可以在代码中使用表达式ptr
吗? 答案 0 :(得分:17)
ptr[0]
中不会评估表达式sizeof(ptr[0])
。大小将通过在编译时使用ptr[0]
的类型来确定。
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]);