關於一些程式語言裡的遞增運算子
前幾天在教學弟 C 程式,其中有出現最常考學生的遞增運算子,像是下面這樣子:
#include <stdio.h>
main() {
int n = 0;
n = ++n + ++n + ++n + ++n;
printf("%d", n);
}
它算出來的結果是11
跟實際上的結果有些許的不同,一般來說答案應該是:
10 = 1 + 2 + 3 + 4
測試了一下會發現實際上變數中的數值卻是,跟所想的有出入:
11 = 2 + 2 + 3 + 4
什麼? 為什麼1
變成了2
,一定有哪邊有誤會,所以就用 GCC 把組合語言輸出看一下到底哪邊出問題了,發現其中一段程式碼:
; ...
movl $0, -4(%rbp) ; rbp = 0
addl $1, -4(%rbp) ; rbp = 0 + 1 = 1
addl $1, -4(%rbp) ; rbp = 1 + 1 = 2
movl -4(%rbp), %eax ; eax = 2
addl %eax, %eax ; eax = 2 + 2 = 4
addl $1, -4(%rbp) ; rbp = 2 + 1 = 3
addl -4(%rbp), %eax ; eax = 4 + 3 = 7
addl $1, -4(%rbp) ; rbp = 3 + 1 = 4
addl %eax, -4(%rbp) ; rbp = 4 + 7 = 11
; ...
程式在遞增並相加的時候有點問題,所以才造成11
這個結果,再試了一下C++也是一樣,所以這時候我在想其他語言應該沒有這個問題吧? 所以就多試了一些語言,像是 PHP 及 Javascript,另外還多試了 Visual C++ 及 C#,執行結果如下:
PHP
<?php
$n = 0;
$n = ++$n + ++$n + ++$n + ++$n;
// 10 = 1 + 2 + 3 + 4
echo $n;
Javascript
'use strict';
var n = 0;
n = ++n + ++n + ++n + ++n;
// 10 = 1 + 2 + 3 + 4
console.log(n);
Visual C++
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
int n = 0;
n = ++n + ++n + ++n + ++n;
// 16 = 4 + 4 + 4 + 4
printf("%d", n);
return 0;
}
C Shape
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int n = 0;
n = ++n + ++n + ++n + ++n;
// 10 = 1 + 2 + 3 + 4
System.Console.Write(n);
}
}
}
結論
從測試的結果來看 PHP、Javascript 及 C# 與預期的答案相符,不過 Visual C++ 的結果就不是這樣了,Visual C++ 算出來是16
,不過形成原因也比較滿合理的,所以這還是各家編譯器設計的問題。
但 C/C++ 的結果是 feature 嗎...? XD
但其實這是所謂的未定義行為(Undefined Behavior),因每個語言的編譯程式的設計不同所造成問題,而在語言中也沒有明確定義所造成的行為。但是正常來說只要知道因起問題的程式是甚饃就可以了,但是不知道為什麼在學校裡的教授卻特別喜愛拿出老考學生,要求學生去背編譯結果這是沒有意義的。
但除了運算子的問題之外還有其他問題也是歸類於未定義行為,可以參考以下的連結。
Update: 2014/03/16