Zan*_*ynx 75 c c++ standards language-lawyer
我已经看过它多次断言现在C++标准不允许使用以下代码:
int array[5];
int *array_begin = &array[0];
int *array_end = &array[5];
Run Code Online (Sandbox Code Playgroud)
&array[5]
在这种情况下是合法的C++代码吗?
如果可能的话,我想要一个参考标准的答案.
知道它是否符合C标准也很有趣.如果它不是标准的C++,为什么决定做出从不同的治疗呢array + 5
还是&array[4] + 1
?
Ada*_*eld 40
是的,这是合法的.从C99标准草案:
§6.5.2.1,第2段:
后缀表达式后跟方括号中的表达式
[]
是数组对象元素的下标名称.下标操作符的定义[]
是E1[E2]
相同(*((E1)+(E2)))
.由于适用于二元+
运算符的转换规则,ifE1
是一个数组对象(等效地,指向数组对象的初始元素的指针)并且E2
是一个整数,因此E1[E2]
指定E2
-th元素E1
(从零开始计数).
§6.5.3.2,第3段(强调我的):
一元运算
&
符产生其操作数的地址.如果操作数的类型是"" 类型 "",结果类型为""指针键入 "".如果操作数是一元运算*
符的结果,则不会对该运算符和&
运算符进行求值,结果就好像两者都被省略,除了对运算符的约束仍然适用且结果不是左值.类似地,如果操作数是[]
运算符的结果,那么&运算符和由此*
隐含的一元运算符都不会[]
被计算,结果就好像&
运算符被移除并且[]
运算符被更改为+
运算符.否则,结果是指向由其操作数指定的对象或函数的指针.
§6.5.6,第8段:
当一个具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型.如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式.换句话说,如果表达式
P
指向i
数组对象的-th元素,则表达式(P)+N
(等效地N+(P)
)和(P)-N
(其中N
有值n
)分别指向数组对象的i+n
第-th和i?n
-th元素,前提是它们存在.此外,如果表达式P
指向数组对象的最后一个元素,则表达式(P)+1
指向数组对象的最后一个元素之后,如果表达式Q
指向一个超过数组对象的最后一个元素,则表达式(Q)-1
指向最后一个元素数组对象.如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义.如果结果指向数组对象的最后一个元素之后,则不应将其用作*
已计算的一元运算符的操作数.
请注意,标准明确允许指针将一个元素指向数组末尾,前提是它们未被解除引用.通过6.5.2.1和6.5.3.2,表达式&array[5]
相当于&*(array + 5)
,它等于(array+5)
一个超过数组末尾的表达式.这不会导致取消引用(6.5.3.2),因此它是合法的.
jal*_*alf 38
你的例子是合法的,但只是因为你实际上并没有使用越界指针.
让我们首先处理超出界限的指针(因为这是我最初解释你的问题的方式,之前我注意到这个例子使用了一个过去的结束指针):
通常,您甚至不允许创建越界指针.指针必须指向数组中的元素,或者指向结尾的元素.无处.
甚至不允许指针存在,这意味着你显然也不允许取消引用它.
以下是该标准对该主题的评价:
5.7:5:
当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型.如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式.换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+ N(等效地,N +(P))和(P)-N(其中N具有值n)指向分别为数组对象的第i + n和第i-n个元素,只要它们存在.此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向一个超过数组对象的最后一个元素,如果表达式Q指向一个超过数组对象的最后一个元素,表达式(Q)-1指向数组对象的最后一个元素.如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定.
(强调我的)
当然,这是针对运营商+的.所以,为了确保,这是标准关于数组下标的内容:
5.2.1:1:
表达式
E1[E2]
相同(通过定义)*((E1)+(E2))
当然,有一个明显的警告:你的例子实际上并没有显示越界指针.它使用"一个结束"指针,这是不同的.允许指针存在(如上所述),但据我所知,标准对解除引用它没有任何说明.我能找到的最接近的是3.9.2:3:
[注意:例如,超过数组末尾的地址(5.7)将被视为指向可能位于该地址的数组元素类型的无关对象. - 尾注]
在我看来,暗示是的,你可以在法律上取消引用它,但读取或写入该位置的结果是未指定的.
感谢ilproxyil在这里纠正最后一点,回答你问题的最后部分:
array + 5
实际上并没有取消引用任何东西,它只是创建一个指向一个结尾的指针array
.&array[4] + 1
dereferences
array+4
(这是非常安全的),获取该左值的地址,并向该地址添加一个,这会产生一个一个接一个的指针(但该指针永远不会被解除引用.&array[5]
取消引用数组+5(据我所知,这是合法的,并导致"数组元素类型的无关对象",如上所述),然后获取该元素的地址,这似乎也足够合法.所以他们不会做同样的事情,尽管在这种情况下,最终结果是一样的.
Tyl*_*nry 17
这是合法的.
根据C++的gcc文档,&array[5]
是合法的.在C++ 和C中,您可以安全地在数组末尾处理一个元素 - 您将获得一个有效的指针.所以&array[5]
表达是合法的.
但是,即使指针指向有效地址,尝试取消引用指向未分配内存的指针仍然是未定义的行为.因此,即使指针本身有效,尝试取消引用该表达式生成的指针仍然是未定义的行为(即非法).
实际上,我想它通常不会导致崩溃.
编辑:顺便说一下,这通常是如何实现STL容器的end()迭代器(作为指向一端的指针),这样就可以很好地证明这种做法是合法的.
编辑:哦,现在我看到你并没有真正询问是否持有指向该地址的指针是合法的,但是如果获得指针的确切方法是合法的.我会按照其他问题回答.
我不相信它是非法的,但我确实认为&array [5]的行为是不确定的.
5.2.1 [expr.sub] E1 [E2]与*((E1)+(E2)相同(按定义)
5.3.1 [expr.unary.op]一元*运算符...结果是一个左值,引用表达式指向的对象或函数.
此时你有未定义的行为,因为表达式((E1)+(E2))实际上没有指向一个对象,标准确实说明了结果应该是什么,除非它确实如此.
如其他地方所述,array + 5
并且&array[0] + 5
是获得超出数组末尾的指针的有效且定义良好的方法.
除了上面的答案,我还会指出运算符&可以为类重写.因此,即使它对POD有效,对于你知道无效的对象也许不是一个好主意(很像首先重写operator&()).
归档时间: |
|
查看次数: |
6741 次 |
最近记录: |