这种未定义行为的基本原理是什么?
What is the rationale for this undefined behavior?
Warning[...]: undefined behavior: the order of volatile accesses is undefined in this statement x.cpp xxx
为什么这行是未定义的行为?
case 2:
Vdda = 3.3 * (*VREFINT_CAL) / ADC_DR->DATA;
其中 declarations/initializations 是:
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 元素的访问顺序可能会有所不同? (什么情况)
但是运算符的优先级相同,难道不应该保证从左到右进行计算吗?
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;
Warning[...]: undefined behavior: the order of volatile accesses is undefined in this statement x.cpp xxx
为什么这行是未定义的行为?
case 2:
Vdda = 3.3 * (*VREFINT_CAL) / ADC_DR->DATA;
其中 declarations/initializations 是:
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 元素的访问顺序可能会有所不同? (什么情况)
但是运算符的优先级相同,难道不应该保证从左到右进行计算吗?
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;