一、運(yùn)算符優(yōu)先級(jí)
前兩天我在園子的首頁(yè)看到一篇隨筆,隨筆地址已經(jīng)找不到了(著實(shí)有點(diǎn)抱歉),不過(guò)其中的一個(gè)代碼片段還是記得一二,大概如下:
Thread t = null; string message = "I'm ..." + t == null ? "And ..." : ""; Console.WriteLine(message);
代碼很少,但我想有不少人會(huì)中招,最關(guān)鍵的問(wèn)題就是運(yùn)算符的優(yōu)先級(jí)了。這里是 C#參考 運(yùn)算符,根據(jù)這份優(yōu)先級(jí)順序,我總結(jié)出以下規(guī)律:
最高:.(點(diǎn)) () [] i++ i--
其次:?jiǎn)文窟\(yùn)算符(i++ 高于 ++i)
然后:先算乘除余、后算加減、最后位移、比大小(大于小于 高于 相等和不等)
接下來(lái):Not And Or ( 即 ! , && , || )
最后:三目 、賦值 和 Lambda
這份優(yōu)先級(jí)順序已經(jīng)包含了大部分運(yùn)算符,只要不是遇到很變態(tài)的問(wèn)題,我想應(yīng)該可以搞定。
二、編譯器的貪心原則
該話題源于《java深入解析——透析Java本質(zhì)的36個(gè)話題》。(雖然源于Java話題,但同樣適用于C#、已驗(yàn)證)
三個(gè)加號(hào)該如何運(yùn)算呢? 代碼片段如下:
int i = 10; int j = 20; int k = i +++ j; // 代碼未格式化,但可以正常編譯運(yùn)行 Console.WriteLine(k); // 輸出結(jié)果是多少呢? 是 30 還是 31?
我們先不看答案,先把代碼格式化一下、看看結(jié)果:
int k = i++ + j; // 格式化后的結(jié)果,為何不格式化成 i + ++j; 呢? 答案是:編譯器的貪心原則
貪心原則:編譯器在分析符號(hào)的時(shí)候會(huì)盡可能多的結(jié)合有效的符號(hào)。
因?yàn)?ldquo;+” 和 “++”都是有效的符號(hào),但是“+++”不是有效的符號(hào),因此,將表達(dá)式解析為了:i++ + j。
再舉個(gè)例子:a--b,本意為a - 負(fù)b。但是編譯器解析為了 a-- b,最終編譯不通過(guò)。所以代碼應(yīng)該這樣寫(xiě):a - -b;
三、 i++你真的理解了嗎?
這個(gè)例子我第一次看到、是在一份筆試試卷上,如果你能說(shuō)出下面這個(gè)代碼片段的正確答案,那這部分就沒(méi)有必要看了,如果不能也許你走入了一個(gè)“誤區(qū)”。
int i = 0; i = i++; Console.WriteLine(i); // 1 or 0?
我在記憶 i++和++i 的時(shí)候,不是記憶“先加”還是“后加”的,而是直接記住“運(yùn)算結(jié)果”。
i++的運(yùn)算結(jié)果:表達(dá)式的值等于 i ,i的值自加1.
++i的運(yùn)算結(jié)果:表達(dá)式的值等于 i+1,i的值自加1.
也就是說(shuō):
i = i++; // 右側(cè)表達(dá)式的結(jié)果等于i本身,然后執(zhí)行賦值運(yùn)算符賦值,最后得到的i的結(jié)果還是 i本身.
補(bǔ)充:
一樓評(píng)論里給出了疑問(wèn),對(duì)于該疑問(wèn)我最簡(jiǎn)單的答案是:誤區(qū)——i++的運(yùn)算結(jié)果:表達(dá)式的值等于 i ,i的值自加1。注意這里說(shuō)的是運(yùn)算結(jié)果,
表達(dá)式的值就等于i了,之后沒(méi)有再進(jìn)行自加1,而i的值進(jìn)行了自加1。
讓我們看一下IL代碼吧,看完之后也許一切疑問(wèn)都將煙消云散:
.method PRivate hidebysig static void Main(string[] args) cil managed{ .entrypoint // 代碼大小 17 (0x11) .maxstack 3 .locals init ([0] int32 i) IL_0000: nop IL_0001: ldc.i4.0 // 將字面常量 0 入棧 棧結(jié)果:{0} IL_0002: stloc.0 // 將棧頂值賦值給第0個(gè)局部變量(即 i),棧頂值出棧 棧結(jié)果: {}, i = 0 IL_0003: ldloc.0 // 將第0個(gè)局部變量的值入棧 (即 0 入棧) 棧結(jié)果:{0} IL_0004: dup // 復(fù)制棧頂值 棧結(jié)果:{0、0} IL_0005: ldc.i4.1 // 將字面常量 1 入棧 棧結(jié)果:{0、0、1} IL_0006: add // 將棧頂?shù)膬蓚€(gè)值相加,棧頂?shù)膬蓚€(gè)值出棧,計(jì)算結(jié)果入棧 棧結(jié)果:{0、1} IL_0007: stloc.0 // 將棧頂值賦值給第0個(gè)局部變量(即 i),棧頂值出棧 棧結(jié)果:{0}, i = 1 到此,完成 i++ 操作 IL_0008: stloc.0 // 將棧頂值賦值給第0個(gè)局部變量(即 i),棧頂值出棧 棧結(jié)果:{}, i = 0 到此,完成 = 賦值操作 由此可以看出最終執(zhí)行賦值操作的值其實(shí)就是最先入棧的 i 的值。 IL_0009: ldloc.0 IL_000a: call void [mscorlib]System.Console::WriteLine(int32) IL_000f: nop IL_0010: ret} // end of method Program::Main
對(duì)于i++的問(wèn)題,我覺(jué)得還是記運(yùn)算結(jié)果為好,就像上面所總結(jié)的那樣。
四、零除以零等于什么?
有人也許會(huì)說(shuō):“小學(xué)生都知道啊,0不能做除數(shù)”,如果是這樣那恭喜你,答對(duì)了。但是只能得6分(滿分10分)。為啥呢?看代碼吧!
static void Main(string[] args) { float x = 0; float result = x / x; Console.WriteLine(result); // 輸出結(jié)果:非數(shù)字 Console.Read(); }此話題只是個(gè)引子,更多信息請(qǐng)參見(jiàn):Single.NaN。
坑人的運(yùn)算符,確實(shí)很坑人啊。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注