通过reinterpret_cast进行未对齐访问 [英] Unaligned access through reinterpret_cast

查看:266
本文介绍了通过reinterpret_cast进行未对齐访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在讨论,试图通过 reinterpret_cast 来确定C ++中是否允许未对齐访问。我认为不是,但我有麻烦找到正确的部分的标准,确认或反驳。我一直在看C ++ 11,但如果更清楚,我会对另一个版本没关系。



C11中未定义访问未定义。 C11标准的相关部分(第6.3.2.3段7):


指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针没有正确对齐的引用类型,行为是未定义的。


由于未对齐访问的行为是未定义的,一些编译器(至少GCC)认为这意味着生成需要对齐数据的指令是可以的。大多数时候,代码仍然适用于未对齐的数据,因为大多数x86和ARM指令这些天使用未对齐的数据,但有些不工作。具体来说,一些向量指令不会,这意味着随着编译器更好地生成优化的指令,与旧版本的编译器一起工作的代码可能不适用于较新的版本。当然,有些架构(像MIPS )对于未对齐的数据也不会有效。



C ++ 11 当然更复杂。 §5.2.10,第7段说:


对象指针可以显式转换为不同类型的对象指针。当指向 T1 类型的prvalue v 转换为类型指向cv T2 ,则结果是 static_cast< cv T2 *>(static_cast< cv void *>(v)) code> T1 和 T2 是标准布局类型(3.9)和 T2 不比 T1 更严格,或者如果任一类型是 void 。将指向 T2 的类型指针的类型转换为类型指针 c> T1 T2 是对象类型,其中 T2 的对齐要求比 T1 更严格)并返回到其原始类型,产生原始指针值。


请注意,最后一个字是unspecified,而不是undefined。 §1.3.25将未指定的行为定义为:


行为,对于一个格式良好的程序结构和正确的数据,实现



[注意:不需要实现来记录哪些行为发生。可能行为的范围通常由本国际标准描述。 - 结束注释]


除非我缺少某些内容,否则该标准并不实际描述范围在这种情况下,这似乎向我表明一个非常合理的行为是实现C(至少由GCC):不支持他们的可能行为。这意味着编译器可以自由地假定未对齐的访问不发生,并且发出可能不对未对齐的内存使用的指令,就像对C一样。



人I然而,讨论这一点有不同的解释。他们引用§1.9第5段:


执行一个精心设计的程序的一个合格的实现应该产生与可能的使用相同的程序和相同的输入来执行抽象机的相应实例。然而,如果任何这样的执行包含未定义的操作,则该国际标准不要求执行具有该输入的程序的执行(即使关于第一未定义操作之前的操作)。


< blockquote>

由于没有未定义行为,他们认为C ++编译器没有权利假定未对齐的访问不会发生。



那么,在C ++中通过 reinterpret_cast 是否是不对齐的访问?

编辑:通过访问,我的意思是实际加载和存储。像

  void unaligned_cp(void * a,void * b){
* reinterpret_cast< volatile uint32_t *> (a)=
* reinterpret_cast< volatile uint32_t *>(b);
}

内存分配方式实际上不在我的范围内可以从任何地方的数据调用),但 malloc 和堆栈上的数组都是可能的候选人。



编辑2

解决方案

查看3.11的源代码(,C ++标准,节和段落) / 1:


对象类型具有对齐要求(3.9.1,3.9.2)该类型可以被分配。


在评论中有一些争论关于什么构成分配一个类型的对象。但是,我相信以下参数无论如何解决都是有效的:



* reinterpret_cast< uint32_t *>(a)。如果这个表达式不会引起UB,那么(根据严格别名规则)必须有一个 uint32_t (或 int32_t )在此语句之后的给定位置。无论对象是否已存在,或者此写入创建它,都没关系。



根据上述标准报价,具有对齐要求的对象只能存在于正确对齐状态。



因此,任何尝试创建或写入一个未正确对齐的对象会导致UB。


I'm in the middle of a discussion trying to figure out whether unaligned access is allowable in C++ through reinterpret_cast. I think not, but I'm having trouble finding the right part(s) of the standard which confirm or refute that. I have been looking at C++11, but I would be okay with another version if it is more clear.

Unaligned access is undefined in C11. The relevant part of the C11 standard (§ 6.3.2.3, paragraph 7):

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

Since the behavior of an unaligned access is undefined, some compilers (at least GCC) take that to mean that it is okay to generate instructions which require aligned data. Most of the time the code still works for unaligned data because most x86 and ARM instructions these days work with unaligned data, but some don't. In particular, some vector instructions don't, which means that as the compiler gets better at generating optimized instructions code which worked with older versions of the compiler may not work with newer versions. And, of course, some architectures (like MIPS) don't do as well with unaligned data.

C++11 is, of course, more complicated. § 5.2.10, paragraph 7 says:

An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type "pointer to T1" is converted to the type "pointer to cv T2", the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

Note that the last word is "unspecified", not "undefined". § 1.3.25 defines "unspecified behavior" as:

behavior, for a well-formed program construct and correct data, that depends on the implementation

[Note: The implementation is not required to document which behavior occurs. The range of possible behaviors is usually delineated by this International Standard. — end note]

Unless I'm missing something, the standard doesn't actually delineate the range of possible behaviors in this case, which seems to indicate to me that one very reasonable behavior is that which is implemented for C (at least by GCC): not supporting them. That would mean the compiler is free to assume unaligned accesses do not occur and emit instructions which may not work with unaligned memory, just like it does for C.

The person I'm discussing this with, however, has a different interpretation. They cite § 1.9, paragraph 5:

A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

Since there is no undefined behavior, they argue that the C++ compiler has no right to assume unaligned access don't occur.

So, are unaligned accesses through reinterpret_cast safe in C++? Where in the specification (any version) does it say?

Edit: By "access", I mean actually loading and storing. Something like

void unaligned_cp(void* a, void* b) {
  *reinterpret_cast<volatile uint32_t*>(a) =
    *reinterpret_cast<volatile uint32_t*>(b);
}

How the memory is allocated is actually outside my scope (it is for a library which can be called with data from anywhere), but malloc and an array on the stack are both likely candidates. I don't want to place any restrictions on how the memory is allocated.

Edit 2: Please cite sources (i.e., the C++ standard, section and paragraph) in answers.

解决方案

Looking at 3.11/1:

Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated.

There's some debate in comments about exactly what constitutes allocating an object of a type. However I believe the following argument works regardless of how that discussion is resolved:

Take *reinterpret_cast<uint32_t*>(a) for example. If this expression does not cause UB, then (according to the strict aliasing rule) there must be an object of type uint32_t (or int32_t) at the given location after this statement. Whether the object was already there, or this write created it, does not matter.

According to the above Standard quote, objects with alignment requirement can only exist in a correctly aligned state.

Therefore any attempt to create or write an object that is not correctly aligned causes UB.

这篇关于通过reinterpret_cast进行未对齐访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆