1

我正在尝试使用 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是 is的实际值。0x10020这是从HARDWARE REGISTER

我们感兴趣的值spm->num_streamsBIT27: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_countsis of uint32_t type)

spm->num_streams is of type uint16_t

有人可以解释为什么会这样吗?

4

2 回答 2

2

你是“假设”规则的受害者。编译器不知道您正在从硬件寄存器中读取数据。当你写:

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最有可能的是,您可以通过 volatile 指针进行 volatile 或“清洗”读取来解决此问题。

而不是MkIV->stream_counts,使用*(volatile uint16_t *)&MkIV->stream_counts.

于 2013-09-24T00:25:26.790 回答
1

由于限定stream_countsvolatile修复了问题,那么您似乎受到了as-if规则的影响,该规则基本上说,如果编译器可以修改程序,只要它可以确定它不会影响可观察的行为。cppreference在这里有一个很好的解释,虽然它是针对C++编写的,但它大部分也适用于C。代码示例特别具有指导意义。

C++ 标准草案不同,C99 标准草案仅明确引用索引中的as-if规则,该规则指向第3段中的5.1.2.3 程序执行部分(强调我的):

在抽象机中,所有表达式都按照语义的规定进行评估。如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。

它确实在某些示例中使用了as if短语,从第11段中我们有以下示例:

float f1, f2;
double d;
/* ... */
f1 = f2 * d;

以下文字说(强调我的):

如果实现可以确定结果与使用双精度算术执行的结果相同,则可以使用单精度算术执行乘法

如果我们看一下6.7.3 Type qualifiers6段说(强调我的):

具有 volatile 限定类型的对象可能会以实现未知的方式被修改或具有其他未知的副作用。因此,任何引用此类对象的表达式都应严格按照抽象机的规则进行评估,如 5.1.2.3 中所述。此外,在每个序列点,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改。116)[...]

volatile这限制了编译器可以针对合格对象采取的捷径。第55.1.2.3节涵盖了对一致性实现的最低要求是.

于 2013-09-24T02:05:25.377 回答