这种未定义行为的理由是什么

What is the rationale for this undefined behavior?

本文关键字:理由 是什么 未定义      更新时间:2023-10-16

警告[…]:未定义的行为:此语句中未定义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_CALADC_DR->DATA(,因此顺序很重要。也许这对你来说并不重要,但对编译器来说肯定很重要。

为了解决这个问题,使用一些临时的,只是添加一个中间序列点:

short a = *VREFINT_CAL;
unsigned b = ADC_DR->DATA;
Vdda = 3.3 * a / b;