我正在尝试使用 gdb 调试 C 程序。我使用的编译标志如下
-fno-strict-aliasing -Wall -DHAVE_CONFIG_H -DNO_OLD_ERF_TYPES -Werror -Wredundant-decls -O2 -DNDEBUG -DBYTESWAP -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -g
我使用的编译器版本是
gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-52)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
有争议的代码是以下行
spm->num_streams = (uint16_t)((MkIV->stream_counts >> 16 ) & 0xfff);
num_streams 的值
with
-fno-strict-aliasing
0xffff (4095)
num_streams 的值
WITHOUT-fno-strict-aliasing
0x1 (1)
现在值得注意的是
mkIV->stream_counts
的实际值是 0x10020
。这是从 HARDWARE REGISTER
读取的
我们感兴趣的值是
spm->num_streams
是 BIT27:BIT16
。因此期望值是 '1'
如果我要更换
spm->num_streams = (uint16_t)((MkIV->stream_counts >> 16 ) & 0xfff);
与
spm->num_streams = (uint16_t)((MkIV->stream_counts & 0xfff0000) >> 16);
然后我得到
0x1(1)
有和没有 -fno-strict-aliasing
的值
MkIV结构中的stream_counts(
MkIV->stream_counts
为uint32_t type
)
spm->num_streams is of type uint16_t
有人可以解释为什么会发生这种情况吗?
您是“假设”规则的受害者。编译器不知道您正在从硬件寄存器中读取数据。当你写下:
spm->num_streams = (uint16_t)((MkIV->stream_counts & 0xfff0000) >> 16);
编译器完全有权这样做:
uint16_t j = MkIV->stream_counts;
MkIV->stream_counts &= 0xfff0000;
MkIV->stream_counts >>= 16;
sp->num_streams = MKIV->stream_counts;
MkIV->stream_counts = j;
最有可能的是,您可以通过使
stream_counts
变得易失性或通过易失性指针“清洗”读取来解决该问题。
不要使用
MkIV->stream_counts
,而使用 *(volatile uint16_t *)&MkIV->stream_counts
。
既然用
stream_counts
限定 volatile
解决了问题,那么您似乎受到了 as-if 规则的影响,该规则基本上说,如果编译器可以修改程序,只要它可以确定它不会影响可观察的行为。 cppreference 在这里有一个很好的解释,尽管它是针对 C++ 编写的,但大部分也适用于 C。代码示例特别有启发性。
与C++标准草案不同,C99标准草案仅明确引用索引中的as-if规则,该规则指向第
5.1.2.3
第第3段中的程序执行(强调我的) ):
在抽象机中,所有表达式都按照语义指定的方式进行计算。一个 实际的实现不需要评估表达式的一部分,如果它可以推断出它的 未使用值并且不会产生所需的副作用(包括由以下原因引起的任何副作用) 调用函数或访问易失性对象)。
它确实在一些示例中使用了 as if 短语,从第 11 段我们有以下示例:
float f1, f2;
double d;
/* ... */
f1 = f2 * d;
下面的文字说(强调我的):
如果我们看一下第如果实现可以确定结果与使用双精度算术执行的结果相同,则可以使用单精度算术执行乘法
6.7.3
具有 volatile 限定类型的对象可能会以未知的方式进行修改 实施或有其他未知的副作用。这限制了编译器对因此任何表达 对于这样的对象,应严格按照抽象机的规则进行评估,如 5.1.2.3 中所述。此外,在每个序列点,最后存储在对象中的值应与抽象机规定的值一致,除非被前面提到的未知因素修改。116)[...]
volatile
限定对象可以采取的快捷方式。第
5.1.2.3
第5段涵盖了符合实施的最低要求是。