这种未定义行为的基本原理是什么?

时间:2015-07-07 12:51:41

标签: c++ 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;

是不是因为编译器不确定易失性元素的访问顺序是不同的? (这是怎么回事)

但不应该确保计算从左到右执行,因为运营商具有相同的优先级?

2 个答案:

答案 0 :(得分:10)

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;

答案 1 :(得分:4)

要理解这一点,您必须了解评估顺序优先级之间的区别。

接受你的表达,例如:

Vdda = 3.3 * (*VREFINT_CAL) / ADC_DR->DATA;

Precedence(和括号)确定如何构建抽象语法树(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;