朋友公司招聘用的一套C#基础面试题,10个码农8个错2个蒙,我也跳坑了…
朋友公司的一套面試題,很有意思,參見如下代碼:
class?Program{static?void?Main(string[]?args){var?t?=?Num();Console.WriteLine(t);Console.ReadLine();}static?int?Num(){int?i?=?10;try{return?i;}finally{i?=?11;Console.WriteLine($"i={i}");}}}請問這段代碼會輸出什么?相信有一點編程經驗的朋友都知道,答案是:output: i=11 ,接下來的一個問題是這個 Num() 方法的返回值是多少, 10 還是 11 ?相信有很多朋友就有點迷糊了,那答案是多少呢?在不運行程序的情況下,我們從 IL 和 匯編角度 來尋找答案。
一:IL 上尋找答案
要想看 IL,可以用 ILSpy 反編譯一下。
.method?private?hidebysig?static?int32?Num?()?cil?managed? {//?Method?begins?at?RVA?0x2074//?Code?size?39?(0x27).maxstack?2.locals?init?([0]?int32?i,[1]?int32)IL_0000:?nopIL_0001:?ldc.i4.s?10IL_0003:?stloc.0.try{IL_0004:?nopIL_0005:?ldloc.0IL_0006:?stloc.1IL_0007:?leave.s?IL_0025}?//?end?.tryfinally{IL_0009:?nopIL_000a:?ldc.i4.s?11IL_000c:?stloc.0IL_000d:?ldstr?"i={0}"IL_0012:?ldloc.0IL_0013:?box?[mscorlib]System.Int32IL_0018:?call?string?[mscorlib]System.String::Format(string,?object)IL_001d:?call?void?[mscorlib]System.Console::WriteLine(string)IL_0022:?nopIL_0023:?nopIL_0024:?endfinally}?//?end?handlerIL_0025:?ldloc.1IL_0026:?ret }?//?end?of?method?Program::Num對比 return i 生成的 IL 代碼,它的做法是將 i 保存到了一個臨時變量 loc.1 處,最后將 loc.1 處的變量返回出去,我們很驚訝的發現 finally 塊中并沒有對 loc.1 處的變量賦值,也就是沒有 stloc.1 指令,綜合下來結果應該是 10, 而不是 11。
不知道可有朋友發現,這里的跳轉指令 IL_0007: leave.s IL_0025 ,貌似直接跳過了 finally 塊,那到底有沒有執行呢?這個從 IL 上看不出,只能從 匯編角度 看啦。
二:查看匯編
接下來祭出windbg,匯編代碼如下:
0:006>?!U?/d?008608a8 Normal?JIT?generated?code ConsoleApp1.Program.Num() Begin?008608a8,?size?aaD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?23: 008608d4?c745e40a000000??mov?????dword?ptr?[ebp-1Ch],0AhD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?26: 008608db?90??????????????nopD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?27: 008608dc?8b45e4??????????mov?????eax,dword?ptr?[ebp-1Ch] 008608df?8945e0??????????mov?????dword?ptr?[ebp-20h],eax 008608e2?90??????????????nop 008608e3?c745ec00000000??mov?????dword?ptr?[ebp-14h],0 008608ea?c745f0fc000000??mov?????dword?ptr?[ebp-10h],0FCh 008608f1?6849098600??????push????860949h 008608f6?eb00????????????jmp?????008608f8D:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?30: 008608f8?90??????????????nopD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?31: 008608f9?c745e40b000000??mov?????dword?ptr?[ebp-1Ch],0BhD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?32: 00860900?b9a8429778??????mov?????ecx,offset?mscorlib_ni+0x142a8?(789742a8)?(MT:?System.Int32) 00860905?e8ea27daff??????call????006030f4?(JitHelp:?CORINFO_HELP_NEWSFAST) 0086090a?8945dc??????????mov?????dword?ptr?[ebp-24h],eax 0086090d?8b0544235d03????mov?????eax,dword?ptr?ds:[35D2344h]?("i={0}") 00860913?8945d4??????????mov?????dword?ptr?[ebp-2Ch],eax 00860916?8b45dc??????????mov?????eax,dword?ptr?[ebp-24h] 00860919?8b55e4??????????mov?????edx,dword?ptr?[ebp-1Ch] 0086091c?895004??????????mov?????dword?ptr?[eax+4],edx 0086091f?8b45dc??????????mov?????eax,dword?ptr?[ebp-24h] 00860922?8945d0??????????mov?????dword?ptr?[ebp-30h],eax 00860925?8b4dd4??????????mov?????ecx,dword?ptr?[ebp-2Ch] 00860928?8b55d0??????????mov?????edx,dword?ptr?[ebp-30h] 0086092b?e820d74c78??????call????mscorlib_ni!System.String.Format(System.String,?System.Object)$##6000545?(78d2e050) 00860930?8945d8??????????mov?????dword?ptr?[ebp-28h],eax 00860933?8b4dd8??????????mov?????ecx,dword?ptr?[ebp-28h] 00860936?e829465b78??????call????mscorlib_ni!System.Console.WriteLine(System.String)$##6000B79?(78e14f64) 0086093b?90??????????????nopD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?33: 0086093c?90??????????????nop 0086093d?58??????????????pop?????eax 0086093e?ffe0????????????jmp?????eaxD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?34: 00860940?8b45e0??????????mov?????eax,dword?ptr?[ebp-20h] 00860943?8d65fc??????????lea?????esp,[ebp-4] 00860946?5f??????????????pop?????edi 00860947?5d??????????????pop?????ebp 00860948?c3??????????????retD:\net5\ConsoleApp1\ConsoleApp1\Program.cs?@?22: 00860949?c745f000000000??mov?????dword?ptr?[ebp-10h],0 00860950?ebee????????????jmp?????00860940為了方便比對,我再把代碼行數給截出來。
一般函數的返回值都是放在 eax 中,所以重點關注下 eax 的賦值部分,仔細觀察它的路徑大概就是下面四句代碼:
mov?????dword?ptr?[ebp-1Ch],0Ah mov?????eax,dword?ptr?[ebp-1Ch] mov?????dword?ptr?[ebp-20h],eax mov?????eax,dword?ptr?[ebp-20h]也就是說,最后的 eax 還是當初的 0Ah= 10, 也和 IL 反映出來的一致,return 操作的底層會將返回值放到一個 臨時區域 = ebp-20h 中,最后返回 臨時區域 中的值。
再回到剛才 IL 部分的疑問,從上面的 jmp 008608f8 指令看,return 的下一步就直接進了 finally 塊,最后執行 RET 彈出下一行代碼指令到 EIP 中完成方法體的執行,大概就是這個樣子。
總結
以上是生活随笔為你收集整理的朋友公司招聘用的一套C#基础面试题,10个码农8个错2个蒙,我也跳坑了…的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在 WASI 上运行 .NET 7 应用
- 下一篇: AutoRest - 具有 C# 和 R