这种未定义行为的理由是什么
What is the rationale for this undefined behavior?
警告[…]:未定义的行为:此语句中未定义volatile访问的顺序x.cpp xxx
为什么这条线是未定义的行为?
case 2:
Vdda = 3.3 * (*VREFINT_CAL) / ADC_DR->DATA;
声明/初始化的位置:
volatile short const *VREFINT_CAL = (short *) 0x1FFFF7BA;
和
volatile STRUCT_ADC_DR *ADC_DR = (STRUCT_ADC_DR*) 0x40012440;
定义者:
typedef struct
{
unsigned DATA : 16;
unsigned : 16;
} STRUCT_ADC_DR;
是因为编译器不确定易失性元素的访问顺序可能不同吗?(情况如何(
但是,难道不应该确保计算是从左到右进行的,因为操作员具有相同的优先级吗?
volatile
向编译器暗示,您正在读取的不是普通内存地址的东西,比如I/O端口。对于两次这样的读取,您很可能希望这些读取按特定顺序进行。
在C和C++中,操作数的求值顺序都没有定义。如果它对你有帮助,可以把除法想象成一个函数调用:
Vdda = 3.3 * divide(*VREFINT_CAL, ADC_DR->DATA);
现在的重点是,对于volatile
,顺序可能很重要,您可能不想把这个决定留给编译器。所以它对此发出了警告。
为了消除警告,只需通过在代码中引入额外的序列点来明确顺序。例如:
short const x = *VREFINT_CAL;
unsigned const y = ADC_DR->DATA;
Vdda = 3.3 * x / y;
要理解这一点,您必须了解评估顺序和优先级之间的区别。
举个例子:
Vdda = 3.3 * (*VREFINT_CAL) / ADC_DR->DATA;
优先级(和括号(决定了如何构建抽象语法树(AST(。结果会是这样的:
=
Vdda
*
3.3
/
*
VREFINT_CAL
->
ADC_DR
DATA
评估的顺序由序列点的存在来决定。您的代码只有一个序列点,位于表达式(;
(的末尾。
因此,任何子表达式的求值顺序都是未指定的。也就是说,编译器可以按照它认为合适的任何顺序进行任何中间计算和内存访问。有些人喜欢认为子表达式是从左到右计算的,但这不是语言的工作方式。
通常情况下,这不会有任何区别,但您的两个子表达式是volatile
(*VREFINT_CAL
和ADC_DR->DATA
(,因此顺序很重要。也许这对你来说并不重要,但对编译器来说肯定很重要。
为了解决这个问题,使用一些临时的,只是添加一个中间序列点:
short a = *VREFINT_CAL;
unsigned b = ADC_DR->DATA;
Vdda = 3.3 * a / b;
相关文章:
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- 不允许功能模板的部分专业化背后的理由是什么?
- 这个错误是什么?似乎没有理由出现
- typeid 运算符忽略 cv 限定符背后的理由是什么?
- 对于类型是类模板专业化的参数,ADL背后的理由是什么
- 当编译器看到 std::vector<Typo> 和 std::vector<struct 拼写错误时发出的诊断之间的差异背后的理由是什么>
- "inline function need to be DEFINED in all tranlation units"背后的理由是什么?
- 延长临时工的使用寿命的理由是什么
- 允许“?”被转义的理由是什么?
- 保留 std::initializer_list 的副本是否安全?理由是什么
- "no diagnostic required"的理由是什么?
- 将所有引用形参设为const的理由是什么?
- 将monotonic_clock重命名为 steady_clock 的理由是什么<chrono>?
- 这种未定义行为的理由是什么
- 不允许绑定到初始化列表中的引用的临时存在于actor结束之后的理由是什么?
- 声明指向*this*对象的指针的优点或理由是什么?