我正在尝试使用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
有人可以解释为什么会这样吗?
答案 0 :(得分:2)
您是'as-if'规则的受害者。编译器不知道你是从硬件寄存器读取的。当你写:
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
。
答案 1 :(得分:1)
由于符合条件stream_counts
和volatile
修复了问题,因此您似乎受到 as-if 规则的影响,该规则基本上表示如果编译器可以修改程序,只要它可以确定它不会影响可观察行为。 cppreference has a nice explanation here虽然它是针对 C ++ 编写的,但大多数也适用于 C 。代码示例特别有用。
与草案C ++标准不同,C99 draft standard仅明确引用索引中的 as-if 规则,该规则指向5.1.2.3
< em>程序执行在段落 3 中说(强调我的):
在抽象机器中,所有表达式都按语义指定的方式进行计算。一个 实际的实现不需要评估表达式的一部分,如果它可以推断出它的那个 没有使用价值,也没有产生所需的副作用(包括由...造成的任何副作用) 调用函数或访问volatile对象。)
它确实在某些示例中使用,如同短语,从 11 段开始,我们有以下示例:
float f1, f2;
double d;
/* ... */
f1 = f2 * d;
以下文字说明(强调我的):
如果实现可以确定结果与使用双精度算术执行 >>
,则可以使用单精度算法执行乘法运算
如果我们查看部分6.7.3
类型限定符段 6 说(强调我的):
具有volatile限定类型的对象可能会以未知的方式进行修改 实施或有其他未知的副作用。 因此任何表达都是指 对于这样的对象,应严格按照抽象机的规则进行评估,如5.1.2.3所述。此外,在每个序列点,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改.116)[...]
它限制了编译器可以对volatile
限定对象进行的快捷方式。第5.1.2.3
段 5 涵盖符合实施的最低要求。