【问题标题】:Evaluation order in ternary operator with increments带增量的三元运算符中的求值顺序
【发布时间】:2020-06-01 17:57:17
【问题描述】:
#define MAX(a,b) ((a)>(b) ? (a) : (b))
   int main(void) {
   int a=2;
   int b=3;

   int c = MAX(a++,b++); // c=((a++)>(b++) ? (a++) : (b++));
   printf("\na= %d", a);// a=3
   printf("\nb= %d", b);//b=5
   printf("\nc= %d", c);//c=4

   a=3;
   b=2;
   cc = MAX(a++,b++); // c=((a++)>(b++) ? (a++) : (b++));

   printf("\na= %d", a); // a=5
   printf("\nb= %d", b); //b=3
   printf("\nc= %d", c); //c=4

return 0;
}

我想知道为什么 c 没有评估为 5。

在我看来,评估顺序应该是:

  1. 首先 a 和 be 在 (a++)>(b++) 中递增
  2. 例如,如果第一个更大,则三元运算符 c=((a++)>(b++) ? (a++) : (b++)),转到(a++),所以a 再次递增。
  3. 三元表达式的结果,它是一个两倍的增量, 应分配给c,然后c 应具有更大的值 增加了两倍,即 5。但是,我得到 4。我怀疑 最后发生更大价值的第二次增量,但我不能 解释原因,因为括号似乎表明 作业在最后。

有什么想法吗?

【问题讨论】:

  • 后缀 ++ 运算符返回之前的值并递增变量。尝试使用前缀 ++ 运算符 ++a 得到 5。
  • 请注意,在某些国家/地区使用带有此类参数的宏会被视为刑事犯罪。
  • 宏不是函数,就像经常说的那样。
  • 关于“括号似乎表明分配在末尾”:括号对副作用没有影响。 ++ 增加对象的效果是一个副作用。它通常与表达式的评估分开,不受括号的影响。 (可能会受到表达式中的序列点和其他排序规则的影响。)
  • 不相关的建议:更喜欢在 printf 的末尾打印"\n",即:printf("%d%s%f\n", ...) 而不是printf("\n%d%s%f", ...)

标签: c conditional-operator


【解决方案1】:

后缀++ 运算符有一个结果和一个副作用a++resulta 在增量之前的值 - 给定

int a = 1;
int x = a++;

x 的值将是 1a 的值将是 2。请注意,将 1 添加到 a 的副作用不必在评估后立即应用 - 它只需在下一个序列点之前应用。

所以,看看

((a++) > (b++)) ? (a++) : (b++)

?: 运算符强制从左到右进行评估,因此首先发生的是 (a++) > (b++) 被评估1。由于a 最初是2 并且b 最初是3,因此表达式的结果为假(0)。 ? 运算符引入了一个序列点,因此 副作用ab 被应用,a 现在是 3b 现在是 4

由于条件表达式的结果是0,我们计算b++。同样,表达式的resultb (4) 的当前值,该值被分配给cb 的副作用在某个时候被应用,当一切都完成时,a3b5c4


  1. 虽然在该表达式中,a++b++ 可能首先被求值,因为> 运算符不强制从左到右求值。

【讨论】:

  • 我如何知道序列点在泛型表达式中的位置?
  • @juancarlosvegaoliver:参见C 2011 Online Draft 的附件 C。这不是官方标准,但已经足够接近了。
【解决方案2】:

让我们以这个声明为例

int c = MAX(a++,b++);

宏替换后会有

int c = (( a++) >( b++ ) ? (a++) : (b++));

变量ab 被初始化为

int a=2;
int b=3;

由于a 小于b,那么第三个表达式(b++) 将作为条件运算符的结果进行计算。在第一个表达式中 ( a++) >( b++ ) ab 增加了。在对第一个表达式求值之后有一个序列点。

所以a 将设置为3b 将设置为4

正如已经说过的,条件运算符的值是第三个表达式(b++) 的值,其中使用了后增量。后自增运算符的值是其操作数在自增前的值。

来自 C 标准(6.5.2.4 后缀递增和递减运算符)

2 后缀++运算符的结果是 操作数。 作为副作用,操作数对象的值是 递增(即,将适当类型的值 1 添加到 它)。

所以条件运算符的值为4。该值将分配给变量c。但作为副作用,b 的值会增加。

因此,在此声明之后,a 将等于 3b 将等于 5c 将等于 4

为清楚起见,此声明

int c = (( a++) >( b++ ) ? (a++) : (b++));

实际上可以用逻辑上等价的方式重写。

int result = a > b;
++a;
++b;

int c;

if ( result != 0 )
{
    c = a++;
}
else
{
    c = b++;
}

【讨论】:

  • 好的,如果在后增量中实际增量只是最后发生的副作用,那么不应该对整个表达式进行分析,就好像根本没有增量,只在最后对评估流程“触及”的值执行挂起的增量,即三元运算的第一个表达式的两个增量和第二个或第三个表达式的第二个增量?这样做,对于 a=2 和 b=3 将导致 c=3,最后 a=2+1=3 和 b=3+2=5。
  • @juancarlosvegaoliver 正如我在评估第一个表达式之后所写的那样,有一个序列点,它是变量 a 和 be 的相应副作用。由于第一个表达式的计算结果为逻辑假,因此条件运算符的值是使用后增量的第三个表达式的值。所以该值是其操作数在递增之前的值。
【解决方案3】:

对于a++,它相当于说

int temp = a;
a = a + 1;
return temp;

所以在a=2,b=3的情况下,我们可以去条件(a++)>(b++),我们可以有一个重写为

int temp1 = a;
a = a + 1;
return temp1;

和b改写为

int temp2 = b;
b = b + 1;
return temp2;

现在因为它只是一个条件语句,我们实际上只是在增量之前评估 a 和 b 的旧值,即 temp1 = 2 和 temp2 = 3,而同时 a 和 b 值已经改变 a = 3 , b = 4。由于 temp1

int temp3 = b;
b = b + 1;
return temp3;

所以现在 b 是 5,而返回的 temp3 是 b 的前一个值,即 4。希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 2010-11-29
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多