Re: [問題] while(*s++ = *t++ );疑問

看板C_and_CPP (C/C++)作者 ( )時間10年前 (2015/06/28 17:05), 10年前編輯推噓8(8032)
留言40則, 4人參與, 最新討論串2/4 (看更多)
※ 引述《BitTorrent (螳勃唬)》之銘言: : 請問一下 : strcpy 中 : while( *s++ = *t++); : 可以copy char arrays : 想請問一下可是++ 優先權 不是大於* : 所以照理講第一個不會被複製到阿?

06/28 12:58,
優先權是指誰先做誰後做,但是該做的事情還是都會執行
06/28 12:58
這裡其實我覺得優先權不是指誰先誰後,而是指一個運算式該怎麼被我們解讀 (後面修文補完文章內容) -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.225.46.73 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1435482334.A.2D2.html

06/28 17:06, , 1F
不小心按錯發文了QQ 還沒寫完, 可以麻煩板主幫刪嗎..
06/28 17:06, 1F

06/28 18:08, , 2F
在文章標題上按大寫E可以修改內文 標題可以用大寫T
06/28 18:08, 2F

06/28 19:14, , 3F
我意指像是 +-*/ 運算子,我們會知道先乘除後加減
06/28 19:14, 3F

06/28 19:15, , 4F
對於運算子的優先權解讀,就是會有誰先做誰後做的順序
06/28 19:15, 4F

06/28 19:16, , 5F
我猜測原po認為優先權較大的情況,可能某運算子被忽略
06/28 19:16, 5F

06/28 19:27, , 6F
在 flat expression 裡 Precedence 用以定義執行順序
06/28 19:27, 6F

06/28 19:44, , 7F
就像 https://goo.gl/2O5Wq9 開頭提及的部分,不過我想
06/28 19:44, 7F

06/28 19:45, , 8F
應該有更加嚴謹的定義吧
06/28 19:45, 8F

06/28 20:11, , 9F
你想要的「更加嚴謹的定義」很多人都回了, 就是如何解讀
06/28 20:11, 9F

06/28 20:12, , 10F
只是在沒有副作用的時候它就是運算順序而已
06/28 20:12, 10F

06/28 20:12, , 11F
這裡因為 ++ 是有副作用的運算子所以才會有這種差別
06/28 20:12, 11F

06/28 20:14, , 12F
今天有事沒法回文了...明天再來修完 既然有推文了文章還
06/28 20:14, 12F

06/28 20:15, , 13F
是留著好了XD
06/28 20:15, 13F

06/28 20:34, , 14F
postfix ++ operator 有 side effect 這我知道 XD
06/28 20:34, 14F

06/28 20:35, , 15F
我上一篇的推文,是假設原 po 已知 side effect 的情況
06/28 20:35, 15F

06/28 20:36, , 16F
提及運算子優先權,決定了編譯後生成的機器碼順序
06/28 20:36, 16F

06/28 20:38, , 17F
畢竟運算子優先權的定義,就是決定了誰該先做誰該後做
06/28 20:38, 17F

06/28 20:39, , 18F
事後想想,side effect 的部分才是最初原 po 要的答案
06/28 20:39, 18F

06/28 20:46, , 19F
但是我的疑惑是,暫時撇開 postfix ++ 的 side effect
06/28 20:46, 19F

06/28 20:47, , 20F
為什麼運算子優先權,suhorng 認為不是先後執行順序
06/28 20:47, 20F

06/28 21:20, , 21F
有三個運算子在 "優先權 => 運算先後" 的命題下會變例外
06/28 21:20, 21F

06/28 21:20, , 22F
&&, ||, ? :
06/28 21:20, 22F

06/28 21:22, , 23F
另外 sizeof 應該也可以算是一個 operator @@
06/28 21:22, 23F

06/28 21:29, , 24F
謝謝,這樣我懂你的意思了,的確用 "如何解讀" 較恰當
06/28 21:29, 24F

06/28 21:29, , 25F
sizeof 的確也是一個 operator 沒錯 XD
06/28 21:29, 25F
講更詳細一點, 我喜歡的 approach (尤其適用於程式語言) 對於一個算式 我們心理想的其實是一棵樹(Abstract Syntax Tree, AST), 為了寫作方便, 寫成平的算式, 並用括弧和 operator precedence, grammar 等等 來建立一個平的字串跟這棵樹之間的關係. + λ / \ / \ 1 * x @ / \ / \ 2 3 x x 1 + 2 * 3 λx. x x 而對於這棵樹有什麼 "意義", 是由我們定義決定的. 最簡單的方式是定義一個解釋函數 value_t eval(tree_t expr) { if (expr == "+") { return intval( eval(expr.left).intval() + eval(expr.right).intval() ); } else if (expression == "*") { ... } } 而當然 eval 函數會適當的決定求值順序以及好好的解釋副作用等等, 例如 ... if (expr == "?:") { val ret = eval(expr.first); if (res.boolval() == true) return eval(expr.second); else return eval(expr.third); } else if (expr == "&&") { val ret = expr(expr.left); if (ret.boolval() == true) return eval(expr.right); else return false; } ... 等等. 我們去看一般語言的定義時, 通常語意都是遞歸的定義在這棵語法樹上, 很少有直接對平平的算式做討論. 當然, 考慮普通的四則運算加減乘除, 由於 +-*/ 這幾個運算子都是 strict, 要說這導致了 +-*/ 的優先權決定了運算順序, 當然不能說是錯的. 但這畢竟是難以一般化的特例. 舉例來說, 函數呼叫其實也 可以是個 operator (我不是說 operator() ), 但函數呼叫要不要把參數先算完 是可以有不同的選擇. 回到 C++. 目前的 C++ 描述的方式是設計 value computation 跟 side effect 兩種. value computation 描述一個值的計算, 而 side effect, 副作用, 一般來說 就是做一些其他的事情像變數修改, 做 I/O 等等. 對於運算子及函數呼叫, C++ 規定了一套該運算子本身的運算和它的參數們之間 value computation, side effect 的偏序關係. 有受到偏序關係規範的我們能說 他們之間誰會先做誰會後做, 未受偏序關係規範的則還有分有可能交替著做, 或者 只能是誰先誰後(不能重疊)等等. 最簡單的例子來說, + 就規定其左右運算元的 value computation 要在 + 的 value computation 之前作完, 不過對於 side effects 則沒有規範. 當兩個 未受偏序關係規制的 side effects 影響到同一個物件時, 就是未定義行為. 回到最一開始的例子, 一個算式如 f1(g()) + f2(h(z(), w())) * f3() 當中, 其實我們副作用發生的順序根本是很寬鬆的定義的. 我們只知道 z,w 的副作用 發生在 h 之前, h 的副作用發生在 f2 之前, g 的副作用發生在 f1 之前, 其餘副作用是有可能任意穿插發生的. 在這種例子中我們很難說優先權就決定了 運算順序. 在副作用之外, 甚至在運算式 e1 + e2 + e3 當中, 三個運算元的 計算順序根本沒有確定規則. 這個算式依據優先權及結合性被解讀為 + / \ + e3 / \ e1 e2 所以我們知道 e3 跟 (e1+e2) 兩者的 value computation 要在最上面的 + 之前算完, 以及 e1 和 e2 的 value computation 要在 e1+e2 的 + 之前算完. 但是 e1, e2, e3 誰先算呢? 不知道. 此外我推文提到的 && || ?: 就是想要反駁說, expr1 && expr2 當中 && 的優先權 即使比 expr1, expr2 算式中的低, 但是 expr2 甚至根本不一定會計算到. 假使 expr1 為 false, (&&) 就計算為 false 了, 那計算順序不就比 expr2 更前面了? 同樣的 expr1 ? expr2 : expr3 這樣的 conditional, expr2 跟 expr3 也一定至 少有一個不會被算, 但 ? : 的結果還是會比不會算的那個先算出來. ※ 編輯: suhorng (36.225.42.192), 07/01/2015 01:21:08

07/01 01:35, , 26F
另一個語法樹結構跟運算順序是兩回事的例子 #1Deo3lyi
07/01 01:35, 26F

07/01 22:04, , 27F
我同意是因為 && 跟 || 有 short circuit evaluation
07/01 22:04, 27F

07/01 22:04, , 28F
我一開始的說法並沒有考量到這些運算子,不夠嚴謹了 XD
07/01 22:04, 28F

07/01 22:09, , 29F
但這也隱含著特定的判斷順序,機器碼仍是有規則的執行
07/01 22:09, 29F
因為語言是定義在 AST 上面的, 我會覺得, 既然 precedence 那些影響 parsing, parsing 出來不同的 AST 又由定義有不同的結果, 當然要說 precedence 在 C++ 中 決定部份東西的先後順序是沒有問題... 不過真的只是部份順序喔. 像我舉的 e1 + e2 + e3 例子即使改成 e1 + e2 * e3, 仍然沒有說 e1, e2, e3 的 side-effects 甚至 value computation 誰先做誰後做. 我們是不知道的. 倒是有興趣的話, Haskell 是 non-strict 的, 通常都用 lazy evaluation 實現, 特點是一個東西真的被需要時才會被求值~ 例如 fib = 1 : 1 : zipWith (+) fib (tail fib) 當我們在 main 裡面 print (take 5 fib) 的時候, 會一個一個從頭開始去找 fib 的內容, 用到的才會去展開算出來, 這裡執行順序就更多的像是執行時才決定了. ※ 編輯: suhorng (36.225.42.192), 07/01/2015 23:01:08 ※ 編輯: suhorng (36.225.42.192), 07/01/2015 23:01:43

07/01 23:49, , 30F
Lilo 你可以去看我貼的那篇, 那篇跟 && || 都無關
07/01 23:49, 30F

07/01 23:49, , 31F
純粹是這個的 side-effect 跟那個的 value computation 間
07/01 23:49, 31F

07/01 23:49, , 32F
誰先誰後的問題
07/01 23:49, 32F

07/01 23:50, , 33F
而就是這個誰先誰後的不明確導致了那種寫法在不同編譯器
07/01 23:50, 33F

07/01 23:50, , 34F
之間會得到不同的結果
07/01 23:50, 34F

07/02 00:03, , 35F
我認同你的說法,你那篇的例子舉的相當恰當,謝啦 :)
07/02 00:03, 35F

07/02 00:06, , 36F
某些有 side effect 情況下,順序的確跟編譯器實作相關
07/02 00:06, 36F

07/02 00:14, , 37F
也許我太過在意的是 codegen 後的機器碼都有特定順序
07/02 00:14, 37F

07/02 00:17, , 38F
偷渡 Haskell 失敗QQ (X
07/02 00:17, 38F

07/02 00:48, , 39F
對 Haskell 不懂,用過 boost::phoenix 也能 lazy 求值
07/02 00:48, 39F

07/02 00:48, , 40F
我知道你要講的意思啦 :)
07/02 00:48, 40F
文章代碼(AID): #1LZxZUBI (C_and_CPP)
討論串 (同標題文章)
文章代碼(AID): #1LZxZUBI (C_and_CPP)