今天發現軟體中一個很奇怪Bug,究其原因是沒有正確理解“等號和異常”的問題,來看下面這段代碼:
static void Main(string[] args)
{
int[] arr = new int[3];
int i = 0;
try
{
arr[i++] = doo();
}
catch { }
Console.WriteLine(i);
}
static int doo()
{
throw new Exception();
}
很簡單的邏輯,一個數組和一個int:i。然後給數組賦值,索引器中有i++操作。右邊調用的方法doo會拋出異常。
我起初是這樣錯誤地想:=是賦值操作,肯定是從右往左進行的,如果右面先出了異常,那麼很自然左面的代碼都不會運行了,此時會跳至catch快,所以上面的方法最終會輸出0。
正確的結果是輸出1。等號左面的代碼會全部啟動並執行。IL可以說明一切,下面是try{}中的IL:
.try
{
IL_000a: nop
//陣列變數進棧
IL_000b: ldloc.0
//i進棧
IL_000c: ldloc.1
//複製一個i到棧頂
IL_000d: dup
//數字1進棧
IL_000e: ldc.i4.1
//棧頂現在是i+1
IL_000f: add
//賦值給i變數,棧頂現在是變數i
IL_0010: stloc.1
//調用doo方法
IL_0011: call int32 Mgen.Test.Program::doo()
//設定數群組成員,索引值是i的值
IL_0016: stelem.i4
IL_0017: nop
IL_0018: leave.s IL_001f
} // end .try
那為什麼會出現這種誤解呢?原因是如果僅從“賦值”這個操作來說,我們可以想當然得認為就是把右面的資料設定給左面的資料,因此如果右面的資料出了異常,那麼左面是無法擷取到值的,所以會認為左面的邏輯也不會運行。但是從技術實現來看這個賦值操作,實際是先進行左面的操作的,因為賦值操作要Crowdsourced Security Testing道把值賦給誰,所以是左面的資料先進棧的,自然左面的操作會被執行,無論右面有沒有異常。