考虑以下代码:
*((unsigned int*)((unsigned char *)0x400000));
它违反严格的别名吗?
根据严格的别名规则:
对象只能通过左值访问其存储值
具有以下类型之一的表达式:
...
要发生冲突,必须有一个
unsigned char
类型的对象,因此,当使用unsigned int*
访问时,就会发生冲突。现在,
(unsigned char *)0x400000
是否在地址unsigned char
上构成一个0x400000
对象?如果不是,则实际上这里没有存储值为unsigned char
类型的对象,因此使用unsigned int
对其进行访问不会违反该规则。请注意以下有关对象的短语:
宾语
执行环境中的数据存储区域,
其内容可以表示值
注:引用时,一个对象可以解释为具有
特殊类型见6.3.2.1。
因此,由于
(unsigned char *)0x400000
并不构成未签名的char对象引用(据我所知),在所提供的代码中没有unsigned char
类型的对象,因此似乎没有冲突。我对么?
关于@Antti Haapala答案:
如果我们假设将
0x400000
转换为unsigned char*
并转换为unsigned int*
的整数到指针转换在我的系统上都有定义的行为,没有陷阱表示且对齐良好,那是为了填补实现定义的空白从标准:整数可以转换为任何指针类型。除了以前
指定,结果是实现定义的,可能不是
正确对齐,可能未指向所引用的实体
类型,并且可能是陷阱表示
那会改变我的问题的答案吗?
最佳答案
从本质上讲,在处理硬件寄存器时,严格的别名是不适用的,因为委员会显然从未考虑过与硬件相关的编程方案或内存映射的寄存器。
严格的别名仅适用于编译器能够确定内存中实际存储内容的有效类型的情况。如果使用硬件地址并左值访问它,则那里的内容永远不会具有编译器可以识别的有效类型。这意味着在读取数据时,我认为适用于6.5的这一部分:
对于没有声明类型的对象的所有其他访问,该对象的有效类型仅是用于访问的左值的类型。
在您的情况下unsigned int
。请注意,就严格的别名和对硬件的左值访问而言,对unsigned char*
进行中间转换完全是多余的。应该将其删除。
但是,如果您进行写访问,则编译器可以自由地将该地址处的值视为通过写访问使用的有效类型:
如果将值存储在没有声明类型的对象中,则通过
具有非字符类型的左值,则左值的类型变为
该访问以及未修改的后续访问的对象的有效类型
储值。
基本上,有效类型和严格别名的规则在处理硬件地址时没有意义,因此编译器无法对其进行过多处理。此外,除非指针是volatile
限定的,否则此类访问通常没有任何意义,从而阻止了基于严格别名进行优化的任何尝试。
可以肯定的是,在进行任何形式的嵌入式系统编程时,请始终使用-fno-strict-aliasing
进行编译。
关于c - 以下内容是否违反严格的别名,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57866198/