a = (a + b) - (b = a); C++ vs php

5
我一直在寻找并找到了一个公式: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 的输出不同吗?


5
你的C++代码存在未定义行为。 - Mat
2
你确定吗?我在我的编译器(g++)中复制/粘贴了你的代码,但是输出的结果是“20 10”,而不是“10 10”。 - leonm
2
@leonm 如果行为未定义,这并不奇怪。 - JJJ
1
我也在这里尝试过http://www.compileonline.com/compile_cpp_online.php,结果是`20 10`。 - user1646111
为什么这个“未定义行为”显然是操作符优先级的问题? - DevZer0
显示剩余9条评论
3个回答

14
我不确定PHP的规则是什么,但在C++中,单个子表达式的顺序没有严格定义,或者按照技术术语,它是“未指定的”-换句话说,编译器可以在执行a + b之前或之后计算b = a。只要它在减法之前执行a + b和b = 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 中似乎没有任何规则。然而,似乎发生的是子表达式按从左到右的顺序进行评估。但实际上并没有在任何地方指定。 - greyfade

3

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

(注:未定义行为指编译器处理代码时不规定具体行为的情况,可能会导致代码错误或异常)

@FractalizeR 因为 PHP 和 C++ 不是相同的编程语言。因此,在 C++ 中未定义的行为在 PHP 中可能完全定义良好,这使您可以在一行中编写通常需要两行(或更多)的代码。或者,等价的,使用两种不同语言编写的相同代码不一定会执行“相同的操作” :) - Thomas
在PHP中,似乎没有任何东西是完全明确定义的。我看不到任何语言标准,只有一组在线网页说“操作符存在并且它们有点像这样”,以及大量的示例。 - Ira Baxter

2

对于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

这与运算符优先级有关,可能在C语言中也是一样的,也可能不同,这取决于优先级是否会导致意外的行为。


我认为PHP是一种解释性语言,因此它可能会将代码的某些部分转换为数字表达式,从而始终给出相同的结果。与编译语言相比,编译语言使用寄存器和内存地址。 - ST3
2
@user2623967 编译或解释并不影响它。PHP的优势在于只有一个常用的解释器,因此即使是未定义的行为通常也是可预测的(尽管可能会因版本和平台而有所不同)。另一方面,有许多不同的C++编译器,未定义的行为在它们之间变化更大。 - JJJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接