当前位置: 首页 > 知识库问答 >
问题:

“sizeof(arr[0])”会导致未定义的行为吗?

龚跃
2023-03-14

计算数组长度有一个众所周知的模式:

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 * 大小(int) 更好,因为大小(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)"如果一个无效值被赋给指针,则一元< code>*运算符的行为是未定义的。87)"

87):“通过一元< code>*运算符取消引用指针的无效值包括空指针、与所指向的对象类型不适当对齐的地址以及对象在其生存期结束后的地址。”

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

我的问题是:

  • ptr的类型已知且ptr的值未知时,能否在代码中使用表达式
  • 根据C99标准,这种使用是否合理?GNU GCC规范

共有3个答案

汪兴为
2023-03-14

在一般情况下,如果我缺少某些内容,则在 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]中的空点取消引用是否应该在这里产生未定义的行为。

雍焱
2023-03-14

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

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

此外,如果要分配十个整数,则应如下调用malloc

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

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

董飞航
2023-03-14

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

< code>sizeof运算符得出其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号名称。大小由操作数的类型决定。结果是一个整数。如果操作数的类型是可变长度数组类型,则对操作数求值;否则,不会对操作数求值,结果是一个整数常数。

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

 类似资料:
  • 所以我对这段代码有了一些了解: 在下面的所有内容中,我假设编译器不能对或的范围有任何先入为主的概念,初始化器仅用于上面的示例。 如果我在一个32位的整数编译器上编译这个(比如在编译x86的时候),没问题。编译器会简单地使用和作为类型值(不能进一步提升它们),乘法会简单地给出注释所说的结果(模在这种情况下是0x100000000)。 然而,如果我在一个64位整数大小的编译器上编译这个(例如x86-6

  • 问题内容: 我有一个简单的函数,它仅将翻译后的消息从服务器返回到客户端。但是,当我将结果传递给var时,结果显示为未定义。 结果- >未定义(错误) 当我查看标题时,它会给我: 为什么我仍然得到不确定的结果? 问题答案: 您可以将回调函数传递给该函数 然后调用:

  • 我正在使用顶点着色器、片段着色器和少量纹理图集(包含数十个较小精灵的图像)将精灵绘制到屏幕上。我的目标是为整个场景使用单个绘图调用,因此需要创建一个可以根据属性动态选择纹理的着色器。每个纹理图集都按顺序绑定,并发送一个平面属性以确定要使用的纹理,该纹理的区域由发送。 GLSL 3.30规范规定采样器数组需要一个常量表达式作为索引,但以下编译和链接没有错误(在最新的Nvidia驱动程序上): 我不能

  • 尽管标题出现了,但这并不是一个哲学问题。 从未初始化的数组读取 使用错误数据 使用不可移植构造。(即内存分配的细节1) 导致具有的行为 标准没有要求产生可预测的效果 我会称之为“未定义的行为”。但也许我错过了什么(?) null null

  • 对于那些不知道_. allKeys(obj)做什么的人,这里有一个片段 因此,它返回传递给它的对象的属性/方法名称数组。 这应该给我: 属性名称:firstname 值名称:John ------ 属性名称:lastname 值名称:Adams ------ 相反,它给了我: 属性名称:firstname 值名称:un定义 ------ 属性名称:lastname 值名称:un定义 ------

  • 在寻找将s组合为的方法时,我在几篇文章中看到,使用是推荐的方法: 但是,我想知道为什么的进一步用法可以定义行为。 如果它不是,而是一个复杂的类对象,那么访问它肯定也不会被定义,对吗?那么,为什么会是这种情况呢? Edit:为了使我的问题清楚,我想指定我的目标:我想找到一种方法,将几个组合成一个并进一步使用这个double,而不会导致未定义的行为。我不希望指定的值。无论如何,我认为这是不可能的,因为