日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

探究C#訪問(wèn)null字段會(huì)拋異常原因_C#教程

作者:HappyGirl快樂(lè)女孩 ? 更新時(shí)間: 2022-08-22 編程語(yǔ)言

一:舉例說(shuō)明?

namespace ConsoleApp2
{
    internal class Program
    {
        static Person person = null;
 
        static void Main(string[] args)
        {
            var age = person.age;
 
            Console.WriteLine(age);
        }
    }
 
    public class Person
    {
        public int age;
    }
}
 

由于?person?是一個(gè) null 對(duì)象,很顯然這段代碼會(huì)拋異常,那為什么會(huì)拋異常呢?要想找原因,需要從最底層的匯編研究起。

二:異常原理分析

1. 從匯編上尋找答案

可以使用?Visual Studio 2022?的反匯編窗口,觀察?var age = person.age;?處到底生成了什么。

---------------- ?var age = person.age; ? ----------------
?
081D6154 ?mov ? ? ? ? ecx,dword ptr ds:[4C41F4Ch] ?
081D615A ?mov ? ? ? ? ecx,dword ptr [ecx+4] ?
081D615D ?mov ? ? ? ? dword ptr [ebp-3Ch],ecx ?

這三句匯編還是很好理解的,4C41F4Ch?存放的是?person?對(duì)象,?ecx+4?是取 person.age,最后一句就是將 age 放在?ebp-3Ch?棧位置上,接下來(lái)我們來(lái)看下 null 時(shí)的 ecx 到底是多少,截圖如下:

從圖中可以看到,此時(shí)的?ecx=0000000,如果大家了解 windows 的虛擬內(nèi)存布局,應(yīng)該知道在虛擬內(nèi)存的?0~0x0000ffff?范圍內(nèi)是屬于 null 禁入?yún)^(qū),凡是落在這個(gè)區(qū)一概屬訪問(wèn)違例,畫(huà)個(gè)圖就像下面這樣。

到這里原理就搞清楚了,因?yàn)?[ecx+4] = [4] 是落在這個(gè) null 區(qū)所致,?但是。。。。?大家有沒(méi)有發(fā)現(xiàn)一個(gè)問(wèn)題,對(duì),就是這里的?[ecx+4],因?yàn)檫@里有一個(gè)?+4?偏移來(lái)取 age 字段,那我能不能在 person 中多定義一些字段,然后取最后一個(gè)字段從而從?null 區(qū)?沖出去。。。哈哈。

2. 真的可以沖出 null 區(qū)嗎

有了這個(gè)想法之后,我決定在?Person?類(lèi)中定義 10w 個(gè) age 字段,參考代碼如下:

namespace ConsoleApp2
{
    internal class Program
    {
        static Person person = null;
 
        static void Main(string[] args)
        {
            var str = @"public class Person
                        {
                            {0}
                        }";
 
            var lines = Enumerable.Range(0, 100000).Select(m => $"public int age{m};");
 
            var fields = string.Join("\n", lines);
 
            var txt = str.Replace("{0}", fields);
 
            File.WriteAllText("Person.cs", txt);
 
            Console.WriteLine("person.cs 生成完畢");
        }
    }
}
 

代碼執(zhí)行后,Person.cs?就會(huì)如期生成,接下來(lái)讀取?person.age99999?看看有沒(méi)有奇跡發(fā)生,參考代碼如下:

    internal class Program
    {
        static Person person = null;
 
        static void Main(string[] args)
        {
            var age = person.age99999;
 
            Console.WriteLine(age);
        }
    }
 

我去,萬(wàn)萬(wàn)沒(méi)想到,把 ClassLoader 給弄崩了。。。。得,那只能改 20000 個(gè) age 試試看吧,參考代碼如下:

    internal class Program
    {
        static Person person = null;
 
        static void Main(string[] args)
        {
            var age = person.age19999;
 
            Console.WriteLine(age);
        }
    }
 

接下來(lái)我們將斷點(diǎn)放在?var age = person.age19999;?上繼續(xù)看反匯編代碼。

------------- var age = person.age19999; ?-------------
0804657E ?mov ? ? ? ? ecx,dword ptr ds:[49F1F4Ch] ?
08046584 ?mov ? ? ? ? dword ptr [ebp-40h],ecx ?
08046587 ?mov ? ? ? ? ecx,dword ptr [ebp-40h] ?
0804658A ?cmp ? ? ? ? dword ptr [ecx],ecx ?
0804658C ?mov ? ? ? ? ecx,dword ptr [ebp-40h] ?
0804658F ?mov ? ? ? ? ecx,dword ptr [ecx+13880h] ?
08046595 ?mov ? ? ? ? dword ptr [ebp-3Ch],ecx ?

從上面的匯編代碼可以看出幾點(diǎn)信息。

  • 匯編代碼行數(shù)多了。
  • ecx+13880h 沖出了 null 區(qū)(FFFF) 的邊界。

接下來(lái)單步調(diào)試匯編,發(fā)現(xiàn)在?cmp dword ptr [ecx],ecx?處拋了異常。。。

大家都知道此時(shí)的 ecx 的地址是 0 ,從?ecx?上取內(nèi)容肯定會(huì)拋訪問(wèn)違例,而且這段代碼很詭異,一般來(lái)說(shuō)?cmp?之后都是類(lèi)似?jz,jnz?跳轉(zhuǎn)指令,而它僅僅是個(gè)半殘之句。。。

從這些特征看,這是 JIT 故意在取偏移之前嘗試判斷?ecx?是不是 null,動(dòng)機(jī)不純哈。。。。

三:總結(jié)

從這些分析中可以得知,JIT 還是很智能的。

  • 當(dāng)偏移值落在?0~FFFF?禁入?yún)^(qū)內(nèi),JIT 就不生成判斷代碼來(lái)減少代碼體積。
  • 在偏移值沖出了?0~FFFF?禁入?yún)^(qū),JIT 不得不生成代碼來(lái)判斷。

原文鏈接:https://blog.csdn.net/weixin_67336587/article/details/125534894

欄目分類(lèi)
最近更新