2014-09-28 24 views
0

我出的現成的Xcode的C編譯器和Chrome控制檯(Javascript解釋)均不能增加,導致3不同的語言如何處理a = a ++?

片段從我的C:

int a = 3; 
a = a++; 
printf("%d\n",a); 

我讀過, C/C++ [編譯器]的這種代碼的問題與序列點之間的多個修改有關。這個概念是否適用於其他通用語言實現?

+3

詢問一個特定的* *的語言,不是 「不同的語言」。 – user2864740 2014-09-28 03:55:34

+0

如果您使用'a ++'或'a = ++ a'而不是'a = a ++',您將得到4.現在您的代碼產生'3'。 – 2014-09-28 03:55:56

+0

@tangrs:查看[示例](http://ideone.com/gFU3U8)。 – 2014-09-28 04:02:21

回答

1

每種語言都可以自由定義自己的規則。你提到C,而在C中,i = i++是未定義的行爲。這允許編譯器編寫者不支持這個表達式,或者決定如何翻譯它。人們會跑到他們的編譯器,測試一個程序,然後根據他們特定的編譯器發佈一個答案。大多數編譯器編寫者喜歡以某種理智的方式編譯表達式,甚至可能記錄他們的特定實現如何定義未定義行爲(UB),但僅適用於該實現,甚至可能適用於該版本。使用分類爲UB的功能的唯一方法是避免它們;即使編譯,語法實際上也不合法。另一方面,C#選擇定義行爲,並以我認爲對於基於堆棧的實現來說是自然的方式來處理它(當然,對於自然和直觀的東西還有其他觀點,因此跨語言的差異)雖然沒有說C#必須在CLR或基於堆棧的實現上運行。

與其使用C#,我會使用與C#有一些相似之處的可樂.NET編譯器,並且我選擇對前綴和後綴增量使用相同的規則。

如果你可以讀取基於堆棧的操作碼,下面是我如何實現它,並且它在C#中是相同的,據我所知。

假設:

int i = 3; 

這裏是以下行如何編譯:

i = i++; 

i ++在因爲我++是一個後增量操作之前計算爲i到增量。因此,我們將原始值存儲到一個臨時(在.NET CLR中,我們將它存儲在堆棧中)。由於最終分配到左側會發生在所有其他事情之後,並且我們將分配的值在增量之前的開始處是「凍結的」,所以在增量過程中發生的事情並不重要,我們將踩踏在最後。

ldloc 'i' // push pre-increment value (3) to be assigned to left side 
ldloc 'i' // push pre-increment value (3) for the increment expression 
ldc.i4.1 // push constant 1, the amount to increment by 
add   // Do the increment, add top 2 operands, resulting in 4 on stack 
stloc 'i' // pop top of stack (4) into i, now the increment is complete and i is 4 
stloc 'i' // pop the top of stack (3), final assignment to i on left hand side 

4中的i是隻存在於臨時的ghost值。

如果你改變了左手邊的k它是簡單的,但最終的結果在我實際上是不同的:

k = i++; 

由於我們只分配給我一次,沒有丟失或假值。現在

ldloc 'i' 
ldloc 'i' 
ldc.i4.1 
add 
stloc 'i' // pop top of stack (4) into 'i', now the increment is complete and 'i' is 4 
stloc 'k' // pop the top of stack (3) off and assigns to 'k' on left hand side 

滿足K == 3,我== 4

+0

+1表示IL(儘管這是有效的,因爲C#規範說這是IL必須工作的方式,並且不會像C那樣爲編譯器提供迴旋餘地) – user2864740 2014-09-28 04:43:27

+0

同意,儘管我實際上是在展示可口可樂的IL, C#,所以C#的IL可能實際上有所不同,但結果將是相同的。只是一個免責聲明,以防某人檢查並發現變化。 – codenheim 2014-09-28 04:54:21

-2

它將返回3,因爲您正在存儲增量前的值。使用前綴運算符而不是後綴。現在

int a = 3; 
a = ++a; 
printf("%d\n",a); 

它將遞增和打印4.

+0

但是爲什麼在賦值後'a'仍然不會增加*? – user2864740 2014-09-28 03:57:45

+1

@ user2864740它遞增,然後分配前一個值。 – 2014-09-28 03:58:05

+0

@JonathanLonowski在賦值之前是什麼讓增量操作「完成」? – user2864740 2014-09-28 03:58:38

2

在C/C++

a = a++; 
a = ++a 

兩者都會調用未定義的行爲。

C標準規定:

之前和下一序列點的對象應具有其存儲的值由表達式的評估修飾的至多一次之間。此外,只有在確定要存儲的值時才能訪問先前值。

行爲隨着評估順序的規則在不同語言中變化而改變。
在C#的情況下,order of evaluation rule,其中表達式中的每個操作數被評估的順序)爲:

「評估每個子表達式嚴格從左到右」。

這意味着上述兩個表達式都是有效的。在C#中,i++ = ++i始終保證爲false,而++i = i++true。 Java也是如此。

在Python中沒有++--運算符,所以不要這麼想:)。

+0

是'a = ++ b'還是'a = b ++'未定義行爲? – 2014-09-28 04:40:51

+1

@CoolGuy在這種情況下,'a'和'b'代表不同的對象,所以上述聲明不適用。 – user2864740 2014-09-28 04:41:16

+0

@ user2864740,我這麼認爲。感謝您的澄清! – 2014-09-28 04:43:06

0

而同樣不能說爲C/C++(由於相同的序列點內的修改),

此行爲是良好定義的一些語言,如Java,C#,和JavaScript。 (應該真的是一個不同的問題/答案,證明每一種情況下。)

在這三種語言(Java/C#/ JS)後綴++運算符應用於立即的L值,得到原始價值,副作用保證在分配前發生。這是保證對所有表達式進行嚴格的左右評估的結果。我相信perl和這樣的操作符有些怪異的地方(它可能會產生一個參考給變量),但是在這個特殊情況下無法畫出任何東西。其他語言必須在個案的基礎上進行分析,因爲它們是不同規則和保證的不同語言。

相關問題