C++标准是否对取数组索引表达式地址提供保证?含nullptr场景
首先看这段代码:
uint8_t my_array[3] = { 1, 2, 3 }; uint8_t *my_array_a = my_array; uint8_t *my_array_b = nullptr; uint8_t *my_element1 = &my_array_a[0]; uint8_t *my_element2 = &my_array_b[0];
注意:上述代码中所有类型的operator&和operator*均未重载。
在C标准(ISO/IEC 9899:TC2)中,计算my_element2时不会对my_array_b + 0进行解引用,标准原文如下:
类似地,如果操作数是[]运算符的结果,则&运算符和[]隐含的一元*都不会被求值,结果等价于移除&运算符并将[]改为+运算符。
现在的问题是:C++标准是否提供类似的保证(至少针对上述场景)?或者以这种方式计算my_element2是否因涉及解引用nullptr而属于未定义行为(UB)?
需要说明的是,我判断这段代码片段不存在隐藏的UB:将nullptr转换为uint8_t*是合法的,且nullptr + 0明确被定义为返回nullptr。该示例实用性不强,仅用于探讨规则本身。此问题类似Stack Exchange上的相关问题,但特别关注C++中是否存在&[]转换为+的等价保证——本示例未进行越界索引,但仍可能因是否实际解引用而存在UB。
在C++标准中,确实存在与C标准类似的语义保证,&my_array_b[0]不会触发对nullptr的解引用,不属于未定义行为。
根据C标准的定义,E1[E2]等价于*(E1 + E2)(这是C继承自C的核心规则)。但当我们对这个表达式取地址&*(E1 + E2)时,标准明确规定这里的一元*运算符不会被实际求值——因为&和*在这种上下文下会直接抵消,最终结果等价于E1 + E2。
回到你的场景:&my_array_b[0]等价于&*(my_array_b + 0),而由于*不会被求值,所以不会发生对nullptr的解引用操作。同时my_array_b + 0本身是合法的:指针与整数0相加,结果就是原指针值(此处为nullptr),这在C++标准中是明确定义的行为。
因此,uint8_t *my_element2 = &my_array_b[0];这段代码完全符合C++标准,不存在未定义行为。
内容的提问来源于stack exchange,提问作者molenzwiebel

