【问题标题】:a = (a + b) - (b = a); C++ vs phpa = (a + b) - (b = a); C++ 与 php
【发布时间】:2013-08-04 07:54:36
【问题描述】:

我一直在寻找公式:a = (a + b) - (b = a) 它应该交换两个变量(或某些情况下的对象)。但是我用 C++ 和 php 对其进行了测试,这给了我不同的结果。

php:

$a = 10;
$b = 20;
$a = ($a + $b) - ($b = $a);
echo $a, " ", $b;

这打印20 10

C++

int a = 10;
int b = 20;
a = (a + b) - (b = a);
std::cout << a << " " << b;

这打印10 10

代码看起来一样但输出不同,我一直在考虑两个原因:

  1. C++ 代码正在编译,php 正在解释。
  2. 这个公式没有用,因为它会导致未定义的行为。

谁能解释一下,为什么在这种情况下 C++ 和 php 的输出不同?

【问题讨论】:

  • *.com/questions/949433/… - 你的 C++ 代码有未定义的行为。
  • 你确定吗?我在我的编译器 (g++) 中复制/粘贴了您的代码,我得到“20 10”而不是“10 10”。
  • @leonm 如果行为未定义,这并不奇怪。
  • @leonm 我使用了 VS2008,它给了 10 10。所以可能是UB,不同的编译器可能以不同的方式处理代码,所以它只是确认它是UB。
  • 我这里也试过compileonline.com/compile_cpp_online.php 结果是20 10

标签: php c++ undefined-behavior


【解决方案1】:

我不确定 PHP 中的规则是什么,但在 C++ 中,各个子表达式的顺序没有严格定义,或者按照技术术语,它是“未指定的”——换句话说,允许编译器在计算b = a 之前或之后计算a + b。只要它在减法之前执行a + bb = a。使用“未指定”行为允许编译器在某些情况下生成更高效的代码,或者只是可以为某些体系结构构建编译器。

这也意味着,如果您有一个表达式在表达式本身中“重新计算”一个值,并且还在表达式的其他地方使用它,您会得到未定义的行为(简称 UB)。 UB 的意思就是,行为没有定义 - 几乎任何事情都可能发生,包括你所看到的和许多其他替代方案(例如,编译器也被允许产生 42 作为结果,即使逻辑表明答案不会是42 在这种情况下 [这是错误的问题!])。

我还建议,如果你想在 PHP 中交换两个值:

 $t = $a;
 $a = $b;
 $b = $t;

在 C++ 中:

 #include <algorithm>

 std::swap(a, b); 

或者如果你坚持自己写:

 int t = a;
 a = b;
 b = t; 

尝试聪明并在“没有临时变量”的情况下执行它几乎肯定会比使用临时变量慢 - 当然在 C++ 这样的编译语言中 - 在 PHP 这样的解释语言中,创建一个新变量可能增加了一些额外的开销,但与所需的逻辑额外工作相比,它不太可能那么大。

【讨论】:

  • PHP 中似乎没有任何规则。然而,似乎发生的是子表达式以从左到右的顺序计算。但实际上并没有在任何地方指定。
【解决方案2】:

C++ 代码由于未定义的行为而完全损坏。 (在一个序列点读取和写入b)。

【讨论】:

  • @FractalizeR 因为 PHP 和 C++ 不是同一种语言。因此,C++ 中未定义的行为可以在 PHP 中完美定义,让您可以在一行中编写 C++ 中通常需要两个(或更多)的内容。或者,等效地,用两种不同语言编写的相同代码不需要做“同样的事情”:)
  • 目前还不清楚 PHP 中是否有完美的定义。我没有看到任何语言标准,只有一组在线网页说“运营商存在并且他们这样做”,还有很多例子。
【解决方案3】:

对于 PHP:

$a = 10;
$b = 20;
$a = ($a + $b) - ($b = $a);
//executes like thus
$a = (30) - ($b = $a);
$a = (30) - ($b = $a = 10); //new $a still not computed, using older $a
$a = (30) - (10);
$a = 20;
//then, $a=20 and $b = 10

这与Operator Precedence完全相关,这在C中可能相同也可能不同,如果不发生意外行为取决于优先级。

【讨论】:

  • 我认为 php 是解释语言,所以它可能会将部分代码转换为数字表达式,因此它总是给出相同的结果。与编译语言相比,寄存器、内存地址的作用。
  • @user2623967 被编译或解释不会进入它。 PHP 的优点是只有一个常用的解释器,因此即使是未定义的行为也通常是可预测的(尽管它可能因版本和平台而异)。另一方面,有许多不同的 C++ 编译器,它们之间的未定义行为差异更大。