[問題] int x=3,y=1; y+=x-=y; 的問題

看板C_and_CPP (C/C++)作者 (公雞)時間13年前 (2010/08/10 21:28), 編輯推噓4(4099)
留言103則, 9人參與, 最新討論串1/2 (看更多)
int x=3,y=1; y+=x-=y; printf("%d %d\n",x,y); 想問大大們的是... 為什麼最後印出x=2 y=3 我一直以為跑的順序為 step 1. y=y+x; // 跑完這行 x=3 y=4 step 2. x=x-y; // 跑完這行 x=-1 y=4 但後來才發現應該是先跑step2再跑step1 可是不懂為什麼會這樣 真的很謝謝大大 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 123.195.13.64

08/10 21:32, , 1F
http://ppt.cc/_szr 你看到的那些符號都跟+-*/一樣有
08/10 21:32, 1F

08/10 21:33, , 2F
優先權
08/10 21:33, 2F

08/10 21:38, , 3F
還有結合方向
08/10 21:38, 3F

08/10 21:54, , 4F
謝謝大大 感覺學到很多東西 真的很感謝:)
08/10 21:54, 4F

08/10 22:59, , 5F
還滿巧妙的 一行裡面其實只改變 x 和 y 的值各一次
08/10 22:59, 5F

08/10 23:01, , 6F
看起來好像觸及未定義行為 其實又沒有
08/10 23:01, 6F

08/11 00:26, , 7F
其實是未定義的 語法上的結合順序只決定了如何運算
08/11 00:26, 7F

08/11 00:27, , 8F
而運算順序除了有相依時是沒有規定的
08/11 00:27, 8F

08/11 00:28, , 9F
C裡給變數值是運算副作用 因此取xy的值未必會在+-運算存入後
08/11 00:28, 9F

08/11 02:11, , 10F
樓上意思是說,對x-=y取x值,在有些實作可能取到x的前值?
08/11 02:11, 10F

08/11 02:11, , 11F
對x-=y取整個值
08/11 02:11, 11F

08/11 02:56, , 12F
C standard是這樣寫的: An assignment expression has the
08/11 02:56, 12F

08/11 02:57, , 13F
value of the left operand after the assignment
08/11 02:57, 13F

08/11 02:58, , 14F
所以不管x被改過了沒, x-=y傳回的值一定是減過的
08/11 02:58, 14F

08/11 03:19, , 15F
寫是這麼寫啦,但是是否真存在另一種執行程式的策略,能取到
08/11 03:19, 15F

08/11 03:21, , 16F
x-=y之前的x值,並且沒有違背標準規定.
08/11 03:21, 16F

08/11 03:31, , 17F
C裡面不可能 因為=會傳回一個改過的rvalue 而不是變數本身
08/11 03:31, 17F

08/11 03:32, , 18F
C++雖然是傳回lvalue 不過應該有補掉這個洞才對..
08/11 03:32, 18F

08/11 07:29, , 19F
所以說加個括號吧!
08/11 07:29, 19F

08/11 07:32, , 20F
放心吧,C/C++ 都是 well-defined behavior。
08/11 07:32, 20F

08/11 07:32, , 21F
但是最好還是不要寫這種 code。
08/11 07:32, 21F

08/11 07:38, , 22F
i++ + ++i; 會有問題是 i++ 先求值,求值瞬間 parallel
08/11 07:38, 22F

08/11 07:39, , 23F
update i。update 動作完成的時間點可在 i++ 和 ; 之間的
08/11 07:39, 23F

08/11 07:40, , 24F
任意時間點。它會跟 ++i 的 parallel update 的時間範圍交
08/11 07:40, 24F

08/11 07:41, , 25F
錯,所以才會有問題。
08/11 07:41, 25F

08/11 07:42, , 26F
這邊 += 一定要等 -= 的結果,全部都是循序求值。
08/11 07:42, 26F

08/11 08:12, , 27F

08/11 08:12, , 28F
不過我搞錯了,這邊y沒有更動兩次所以沒有這個問題
08/11 08:12, 28F

08/11 08:15, , 29F
y=y+(x-=y)這部份,固然x-=y的部份會是運算完的結果
08/11 08:15, 29F

08/11 08:16, , 30F
但是並沒有保證y+(-=)的y取值是在之前或之後
08/11 08:16, 30F

08/11 08:16, , 31F
不過這邊y並沒有另外更動過所以沒這個問題
08/11 08:16, 31F

08/11 08:17, , 32F
有問題的是再多串一個如x-=y+=x-=y;這樣 最後的x -= (=+...)
08/11 08:17, 32F

08/11 08:17, , 33F
這裡的x可以是一開始運算完的x-=y;或是沒運算過的x或是其他
08/11 08:17, 33F

08/11 08:20, , 34F
即使這符合standard裡assignment的lvalue規範仍會造成未定義
08/11 08:20, 34F

08/11 08:29, , 35F
不過關鍵也不在取值而在是在同時更動同變數兩次或以上就是了
08/11 08:29, 35F

08/11 08:30, , 36F
不管是透過=或<op>=或是++ --,取值有問題的是類似a[i]=i++
08/11 08:30, 36F

08/11 08:31, , 37F
即使=右邊取的值沒問題,不過不能保證左邊的a[i]到底是抓誰
08/11 08:31, 37F

08/11 09:18, , 38F
關於上面提到 C++ 傳回 lvalue 這件事。基本上 assignment
08/11 09:18, 38F

08/11 09:18, , 39F
只有 C 才說可以把 update 給 delay 到下個 sequence
08/11 09:18, 39F
還有 24 則推文
08/11 20:05, , 64F
C 的狀況,以 y += x -= y 來說,x -= y 傳回的是一個臨時
08/11 20:05, 64F

08/11 20:05, , 65F
變數,內含 x - y 的計算結果,是個 rvalue。
08/11 20:05, 65F

08/11 20:06, , 66F
而 x 本身的內容,實際 update 的時間可以 delay 到整個
08/11 20:06, 66F

08/11 20:06, , 67F
full-expression 做完為止才進行。
08/11 20:06, 67F

08/11 20:07, , 68F
但是 C++ 沒說可以這樣,x -= y 要傳回 x 的 lvalue,並且
08/11 20:07, 68F

08/11 20:08, , 69F
x 必須是已經 update 完畢的。
08/11 20:08, 69F

08/11 20:09, , 70F
這有點類似平行的概念,C 的狀況下先算 tmp = x - y。
08/11 20:09, 70F

08/11 20:09, , 71F
然後從 main thread 分出一條 thread 做 x = tmp。
08/11 20:09, 71F

08/11 20:10, , 72F
main thread 做 y += tmp。最後在 full-expression 結尾
08/11 20:10, 72F

08/11 20:10, , 73F
join thread。
08/11 20:10, 73F

08/11 20:10, , 74F
當初這樣設計主要是方便 compiler 做指令排程。
08/11 20:10, 74F

08/11 20:11, , 75F
並不是真的用 thread,就是概念上後面有另一個流程在跑。
08/11 20:11, 75F

08/11 20:17, , 76F
看了一下還真的是回傳rvalue @@
08/11 20:17, 76F

08/11 20:35, , 77F
順帶說明,x-=y+=x-=y; 可以想成同時 update x 的 threads
08/11 20:35, 77F

08/11 20:35, , 78F
有兩條,都是在 ; 的位置 join,所以會有 race condition
08/11 20:35, 78F

08/11 20:36, , 79F
。而所謂的 race condition 會在 compile-time 就決定,每
08/11 20:36, 79F

08/11 20:37, , 80F
次結果都一樣,但是你換 compiler,換參數 O1 O2 O3,可能
08/11 20:37, 80F

08/11 20:37, , 81F
都會造成不同的結果。端看 compiler 怎麼排指令。
08/11 20:37, 81F

08/12 08:31, , 82F
我想你說的是"In all cases, the assignment is sequenced
08/12 08:31, 82F

08/12 08:31, , 83F
after the value computation of the right and left operan
08/12 08:31, 83F

08/12 08:31, , 84F
and before the value computation of the assignment expr.
08/12 08:31, 84F

08/12 08:33, , 85F
吧?這的確限制了你能取得x -= y的值時x已經update過了
08/12 08:33, 85F

08/12 08:33, , 86F
但是問題是它只規範相依性而不是強制的結算點
08/12 08:33, 86F

08/12 08:34, , 87F
所以在sp之前有其他平行對x取值或update時仍然無法確定順序
08/12 08:34, 87F

08/12 09:13, , 88F
不用看到那句,直接看 Assignment operators 的 Semantics
08/12 09:13, 88F

08/12 09:13, , 89F
:An assignment expression has the value of the left
08/12 09:13, 89F

08/12 09:14, , 90F
operand after the assignment, but is not an lvalue.
08/12 09:14, 90F

08/12 09:14, , 91F
C99 的 p91 paragraph 3。
08/12 09:14, 91F

08/12 09:16, , 92F
y += x -= y 這種狀況, x -= y 會得到一個 -= 運算的運算
08/12 09:16, 92F

08/12 09:17, , 93F
結果,後續 y += 的 RHS 是運算結果本身,而不是從 x 取值
08/12 09:17, 93F

08/12 09:21, , 94F
C++03 的話,p.91 的 paragraph 1:The result of the
08/12 09:21, 94F

08/12 09:21, , 95F
assignment operation is the value stored in the left
08/12 09:21, 95F

08/12 09:21, , 96F
operand after the assignment has taken place;
08/12 09:21, 96F

08/12 09:22, , 97F
the result is an lvalue.
08/12 09:22, 97F

08/12 09:26, , 98F
不管 C 還是 C++,在這 case 都不需要在意 x 幾時更新。
08/12 09:26, 98F

08/12 09:28, , 99F
只要把重心放在 -= 這個 operator 的輸出結果是什麼就好。
08/12 09:28, 99F

08/12 09:30, , 100F
你擔心的問題,是 x 再度出現在其它位置,就像你前面舉的
08/12 09:30, 100F

08/12 09:31, , 101F
x-=y+=x-=y,這樣最左邊的 x 會抓到不確定值才需要注意。
08/12 09:31, 101F

08/12 16:40, , 102F
嗯 我講的是那個情況沒錯 原本的y-=x+=y;是不會有這個問題
08/12 16:40, 102F

08/12 16:41, , 103F
所以我以為你在講c++中所有的assignment都不會undefined了:P
08/12 16:41, 103F
文章代碼(AID): #1COLFzSa (C_and_CPP)
討論串 (同標題文章)
文章代碼(AID): #1COLFzSa (C_and_CPP)