nem*_*equ 18 c++ language-lawyer c++11
我正在讨论试图弄清楚C++中是否允许未对齐访问reinterpret_cast.我想不是,但我找不到确认或反驳的标准的正确部分.我一直在看C++ 11,但是如果它更清楚的话,我可以使用另一个版本.
C11中未定义未对齐的访问权限.C11标准的相关部分(第6.3.2.3段,第7段):
指向对象类型的指针可以转换为指向不同对象类型的指针.如果生成的指针未针对引用的类型正确对齐,则行为未定义.
由于未定义访问的行为未定义,因此某些编译器(至少是GCC)认为可以生成需要对齐数据的指令.大多数情况下,代码仍适用于未对齐的数据,因为现在大多数x86和ARM指令都使用未对齐的数据,但有些则没有.特别是,某些向量指令不会,这意味着随着编译器在生成优化指令方面变得更好,使用旧版本编译器的代码可能无法与较新版本一起使用.当然,某些体系结构(如MIPS)与未对齐数据的效果不同.
当然,C++ 11更复杂.§5.2.10,第7段说:
可以将对象指针显式转换为不同类型的对象指针.当prvalue
v类型的"指针T1"被转换为类型"指向cvT2",结果是static_cast<cv T2*>(static_cast<cv void*>(v))如果两个T1和T2是标准布局类型(3.9)和的对准要求T2并不比那些更严格的T1,或者如果任一类型是void.将"指向T1"的类型的prvalue转换为"指向"的类型T2(其中T1和T2是对象类型,并且对齐要求T2不比那些更严格T1)并返回其原始类型,产生原始指针值.未指定任何其他此类指针转换的结果.
请注意,最后一个单词是"未指定",而不是"未定义".§1.3.25将"未指明的行为"定义为:
行为,对于格式良好的程序构造和正确的数据,取决于实现
[ 注意:不需要实现来记录发生的行为.本国际标准通常描述了可能的行为范围.- 结束说明 ]
除非我遗漏了某些内容,否则标准实际上并没有描述这种情况下可能的行为范围,这似乎向我表明,一个非常合理的行为是为C实现的行为(至少由GCC实现):不支持他们.这意味着编译器可以自由地假设未发生未对齐访问并发出可能无法使用未对齐内存的指令,就像它对C一样.
然而,我与之讨论的人有不同的解释.他们引用第1.9段第5段:
执行格式良好的程序的一致实现应该产生与具有相同程序和相同输入的抽象机的相应实例的可能执行之一相同的可观察行为.但是,如果任何此类执行包含未定义的操作,则此国际标准不要求使用该输入执行该程序的实现(甚至不考虑第一个未定义操作之前的操作).
由于没有未定义的行为,他们认为C++编译器无权假设未发生未对齐访问.
那么,reinterpret_cast在C++中通过安全进行未对齐的访问吗?它在说明书(任何版本)中的位置如何?
编辑:通过"访问",我的意思是实际加载和存储.就像是
void unaligned_cp(void* a, void* b) {
*reinterpret_cast<volatile uint32_t*>(a) =
*reinterpret_cast<volatile uint32_t*>(b);
}
Run Code Online (Sandbox Code Playgroud)
如何分配内存实际上超出了我的范围(它适用于可以从任何地方调用数据的库),但是malloc堆栈上的数组都可能是候选者.我不想对内存的分配方式施加任何限制.
编辑2:请在答案中引用来源(即 C++标准,部分和段落).
看3.11/1:
对象类型具有对齐要求(3.9.1,3.9.2),这些要求对可以分配该类型的对象的地址施加限制.
关于究竟是什么构成分配类型对象的评论中存在一些争论.但是我相信无论讨论如何解决,以下论点都有效:
以*reinterpret_cast<uint32_t*>(a)为例.如果此表达式不会导致UB,则(根据严格别名规则)在此语句之后的给定位置必须存在类型uint32_t(或int32_t)的对象.对象是否已经存在,或者这个写入是否创建它并不重要.
根据上述标准引用,具有对齐要求的对象只能以正确对齐的状态存在.
因此,任何创建或写入未正确对齐的对象的尝试都会导致UB.
| 归档时间: |
|
| 查看次数: |
1917 次 |
| 最近记录: |