【问题标题】:Does array subscription count as taking address of object?数组订阅算不算获取对象的地址?
【发布时间】:2017-12-19 10:05:04
【问题描述】:

这个问题的灵感来自answers to this question

以下代码可能会出现未定义的行为:

uint64_t arr[1]; // Uninitialized
if(arr[0] == 0) {

C 标准规定具有自动存储持续时间的未初始化变量具有不确定值,它要么是未指定,要么是trap 表示。它还指定 uintN_t 类型没有填充位,并且值的大小和范围定义良好;所以uint64_t 的陷阱表示是不可能的。

所以我得出结论,未初始化的值本身并不是未定义的行为。怎么读?

6.3.2.1 左值、数组和函数指示符

  1. ...
  2. 除非它是 sizeof 运算符、_Alignof 运算符、一元 & 运算符、++ 运算符、-- 运算符或 the 的左操作数的操作数。运算符或赋值运算符,将不具有数组类型的左值转换为存储在指定对象中的值(不再是左值);这称为左值 转换。 ...

    ... 如果 左值指定一个具有自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未占用其地址),并且该对象未初始化(未使用初始化程序声明并且未对其执行分配使用前),行为未定义。

  3. 除非它是 sizeof 运算符、_Alignof 运算符或 一元 & 运算符,或者是一个字符串字面量,用于初始化一个数组,一个具有 type ‘‘array of type’’ 被转换为类型为 ‘‘pointer to type’’ 的表达式,它指向 到数组对象的初始元素并且不是左值。 如果数组对象有 注册存储类,行为未定义。

问题:下标数组算取对象的地址吗?

以下文字似乎暗示下标数组需要转换为指针,这似乎不可能不带地址:

6.5.2.1 数组下标

约束

  1. 其中一个表达式的类型应为“指向完成对象类型的指针”,另一个 表达式应为整数类型,结果为“类型”类型。

语义

  1. 后缀表达式后跟方括号 [] 中的表达式是下标 指定数组对象的元素。下标运算符[]的定义 是 E1[E2] 与 (*((E1)+(E2))) 相同。由于转换规则 适用于二元 + 运算符,如果 E1 是一个数组对象(等效地,指向 数组对象的初始元素)并且 E2 是整数,E1[E2] 指定 E2- E1 的元素(从零开始计数)。

这使得 §6.3.2.1 第 3 段看起来很奇怪。如果订阅需要转换为指针,数组怎么可能有注册存储类?

【问题讨论】:

  • footnote 121 表示使用寄存器存储类声明的数组的唯一允许操作是sizeof_Alignof。 IE。一点用都没有。当然,否则行为只是未定义,并不意味着实现不能为这种情况提供一些合理且可用的行为。
  • @P.P.我不得不不同意。第一个似乎涵盖了我已经知道的方面,我觉得从这个问题的角度来看,第二个根本没有涵盖语言律师。
  • @AnttiHaapala 这是一个很好的发现。这意味着arr 不能放在寄存器中。

标签: c arrays language-lawyer


【解决方案1】:

是的,根据您在 6.5.2.1 中引用的部分,数组下标算作获取地址。表达式E1 必须有它的地址。

因此 6.3.2.1 中 UB 的特殊情况不适用于数组索引。如果使用数组索引,则数组是否可以以register 存储时长存储无关紧要(获取其地址的变量不能使用register 存储时长)。

您正确地假设读取具有不确定值的未初始​​化 stdint.h 类型,其地址已被占用,不会调用未定义的行为(由 C11 7.20.1.1 保证),但只是未指定行为。该值可以是任何值,并且在多次读取之间可以是不确定的,但它不能是陷阱。

“读取未初始化的变量总是 UB”是一个广泛传播但不正确的神话。 更多信息请参见this answer

【讨论】:

  • 我会引用 this footnote... 即除 sizeof_Alignof 之外的任何其他数组都不能满足“可以使用寄存器存储持续时间存储”
  • 我不明白 7.20.1.1 如何排除陷阱表示。可能存在 C 程序不可见的内存属性,例如校验和位。
  • @AnttiHaapala 在一般情况下,是的,只要数组不使用索引(这是拥有数组的全部意义)。但是,将一个 4 字节的数组存储在 32 位寄存器中当然是有意义的,而不是存储在堆栈中。
  • @AndrewHenle “没有填充位,和一个二进制补码表示”。变量的值使用 all 数据位,没有其他空间。这种意义上的陷阱表示是软件陷阱,例如 SIGSEGV 或 SIGFPE。而内存和硬件不在编程语言标准的范围内。如果硬件内存有某些方面,例如阻止从某个内存读取数据,那么就会产生硬件异常,这是不受编程语言标准规定的。
  • @EricPostpischil 阅读了我第一条评论中的链接。另外,缺陷报告451
猜你喜欢
  • 2013-07-10
  • 1970-01-01
  • 2013-12-22
  • 1970-01-01
  • 2021-12-20
  • 2019-04-26
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
相关资源
最近更新 更多