【问题标题】:Is it undefined behavior to get the address of an array element that is out of bounds?获取超出范围的数组元素的地址是未定义的行为吗?
【发布时间】:2019-11-14 17:01:06
【问题描述】:

假设我们分配了一个包含 10 个元素的字节数组。定义访问范围内的任何元素。

我了解越界读取和写入元素是未定义的行为。获取超出范围的数组元素的地址是未定义的行为吗?

例子:

#include <stdint.h>
#include <string.h>

int main(void)
{
    uint8_t buf[10];
    memset(buf, 0, sizeof(buf));

    // Defined behavior
    uint8_t a_value = buf[9];

    // Defined behavior
    buf[0] = 1;

    // Undefined behavior?
    uint8_t *addr = &buf[10];
}

【问题讨论】:

    标签: c arrays undefined-behavior


    【解决方案1】:

    &amp;buf[10] 是一个特例。您可以在没有 UB 的情况下获取数组的“过去最后一个元素”的地址。但是你不能走得更远或走在第一个元素之前。因此&amp;buf[11]&amp;buf[-1] 是UB。

    每个请求,来自the latest available draft of C18

    6.5.3.2/3 说明&amp;buf[10] 等价于buf+10

    同样,如果操作数是 [] 的结果 运算符,既不计算 & 运算符,也不计算 [] 所隐含的一元 * 并且结果 就好像移除了 & 运算符,而将 [] 运算符更改为 + 运算符。

    而 6.5.6/8 向我们提供了有关 + 行为的信息:

    此外,如果表达式 P 指向 数组对象的最后一个元素,表达式 (P)+1 指向数组的最后一个元素 对象,如果表达式 Q 指向数组对象的最后一个元素,则表达式 (Q)-1 指向数组对象的最后一个元素。如果指针操作数和结果指向 对同一数组对象的元素,或数组对象的最后一个元素,求值 不得产生溢出;否则,行为未定义。

    【讨论】:

    • 实际上,从&amp;buf[10]buf + 10 的转换还需要6.5.3.2p3: 一元&amp; 运算符产生其操作数的地址。如果操作数的类型为“type”,则结果的类型为“pointer to type”。如果操作数是一元 * 运算符的结果,则该运算符和 &amp; 运算符都不会被计算,结果就好像两者都被省略了,除了对运算符的约束仍然适用并且结果不是左值
    【解决方案2】:

    在不取消引用或以任何其他方式使用它(有一些例外)的情况下不获取引用不是未定义行为。

    一个例子:

    int x[5];
    
    printf("%zu\n", sizeof(*(x + 1000));   // no UB
    

    【讨论】:

    • 其实这个特例是可以的,就是数组后面第一项的地址。
    • @P__J__:标准 C 没有提供定义的方法来计算参考。 C 2018 6.5.6 8 说,将 + 与指针一起使用:“如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义。”
    • 然而,在sizeof(*(x + 1000) 中,sizeof 的操作数没有被计算,所以不会发生未定义的行为。结果仅仅是表达式的 type 的大小。如果评估了x + 1000,C 标准将不会定义行为。例如,表达式语句 x + 1000; 的行为不是由 C 标准定义的,即使它的结果未被使用。
    • C 标准的这一特性的一个原因是某些 C 实现必须使用补充数据来管理内存,因此指针不仅仅是对象的地址。它包括其他数据结构,计算指针算术可能涉及在这些结构中查找内容,并且带有无效索引的查找可能会进入未映射的内存或以其他方式阻塞程序。
    • C 标准相当明确;它明确指出,在我提供的引用中“行为未定义”。声明“我不知道任何实现或机器......”只是对您缺乏知识的声明,而不是对解释 C 标准有用的信息或推理。我确实知道必须使用补充数据结构来管理内存中对象的机器。此外,Clang 在指针来源方面变得更加激进,因此即使目标实现具有平坦的地址空间,C 标准未定义的指针算法也可能会中断。
    猜你喜欢
    • 2020-09-26
    • 2016-10-14
    • 2012-02-25
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多