日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

还不明白可空类型原理? 我可要挖到底了

發(fā)布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 还不明白可空类型原理? 我可要挖到底了 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一:背景

1. 講故事

做好自媒體到現(xiàn)在有一個月了,關(guān)注我的兄弟應(yīng)該知道我產(chǎn)出了不少文章,號里的粉絲也多起來了,我也盡最大努力做到有問必回,現(xiàn)在是基礎(chǔ)的、高深的問題都接踵而來,可我也只是一只小菜鳥,想飛也飛不動了(┬_┬),昨天號里有位朋友被面試官問到可空類型的原理,回答的不好,面試官也是,面就面唄,又給不了多少銀子,還動不動就原理,哪有那么多原理,搞得雙方都尷尬????????????。

二:給我鋤頭我要挖到底

這種問題要怎么挖呢?我在之前的文章也聊過,C#代碼到機器碼中間有兩個編譯過程,一個是csc編譯后的IL代碼,一個是jit編譯后的native代碼,所以搞懂IL代碼和native代碼就是我們要深究的方向,我還是把那篇文章的圖拿過來。

為了方便演示,我就定義一個int?類型,接收非null和null兩種情況。

static void Main(string[] args){int? num1 = 10;int? num2 = null;Console.WriteLine("執(zhí)行結(jié)束啦!");Console.ReadLine();}

1. 挖IL代碼

挖IL代碼簡單,用ILSPY小工具就可以了,編譯后生成的IL代碼如下:

.method private hidebysig static void Main (string[] args) cil managed {// Method begins at RVA 0x2048// Code size 36 (0x24).maxstack 2.entrypoint.locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> num1,[1] valuetype [mscorlib]System.Nullable`1<int32> num2)IL_0000: nopIL_0001: ldloca.s 0IL_0003: ldc.i4.s 10IL_0005: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)IL_000a: ldloca.s 1IL_000c: initobj valuetype [mscorlib]System.Nullable`1<int32>IL_0012: ldstr "執(zhí)行結(jié)束啦!"IL_0017: call void [mscorlib]System.Console::WriteLine(string)IL_001c: nopIL_001d: call string [mscorlib]System.Console::ReadLine()IL_0022: popIL_0023: ret } // end of method Program::Main

這IL代碼還是非常易懂的,比匯編簡單多啦(┬_┬),可以看到int ??就是?System.Nullable<int32>?,然后從valuetype?標(biāo)記可以看到這玩意是個值類型,所以把上面的代碼回轉(zhuǎn)成C#代碼就是下面這樣。

{static void Main(string[] args){//int? num1 = 10;//int? num2 = null;Nullable<int> num3 = new Nullable<int>(10);Nullable<int> num4 = new Nullable<int>();Console.WriteLine("執(zhí)行結(jié)束啦!");Console.ReadLine();}

很簡單吧,那怎么輸出num3和num4呢?直接Console.WriteLine就好了。

這里你肯定有一個疑問,為什么num3輸出10,而num4什么都沒輸出呢?哈哈,這是因為Nullable的ToString()被重寫了,再來看下ToString被重寫成啥樣了,代碼如下:

public struct Nullable<T> where T : struct {private bool hasValue;internal T value;[NonVersionable][__DynamicallyInvokable]public Nullable(T value){this.value = value;hasValue = true;}[__DynamicallyInvokable]public override string ToString(){if (!hasValue){return "";}return value.ToString();} }

可以看到ToString方法里要么返回空字符串要么返回你在構(gòu)造函數(shù)中塞入的value,這這么簡單,IL代碼挖到這里就可以了。

2. 挖機器代碼

要看num1和num2的機器代碼,其實也就是看?Nullable<T>?的內(nèi)存布局方式,這里我使用windbg,還是使用?!clrstack -l?查看線程棧。

int? num1 = 10;int? num2 =null;0:007> ~0s ntdll!ZwReadFile+0x14: 00007ffc`ec11aa64 c3 ret 0:000> !clrstack -l OS Thread Id: 0x5364 (0)Child SP IP Call Site ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 21]LOCALS:0x00000018a9dfeaf8 = 0x0000000a000000010x00000018a9dfeaf0 = 0x000000000000000000000018a9dfed08 00007ffcd5b66c93 [GCFrame: 00000018a9dfed08]

從LOCALS中可以看到,num1和num2的線程棧上存放的內(nèi)容分別是0x0000000a00000001和?0x0000000000000000, 不過這值也挺奇怪的,一個是1一個是0。。。我們用 dd 命令把地址轉(zhuǎn)儲出來。

0:000> dd 0x00000018a9dfeaf8 00000018`a9dfeaf8 00000001 0000000a a9dfec08 00000018 0:000> dd 0x00000018a9dfeaf0 00000018`a9dfeaf0 00000000 00000000 00000001 0000000a

在num1的內(nèi)存區(qū)域中有一個十六進制值?0000000a?,這就是十進制的10,那前面的?00000001?是什么東西呢?大家不要忘啦, int?是語法糖, 你現(xiàn)在看的是?Nullable<T>?哈。。。

看清楚啦,這個結(jié)構(gòu)體里面有兩個值類型字段,自然?00000001?就是?hasValue=true啦。?num2也就好理解了,兩個默認值也就是兩個0了。00000000 00000000。

三:有意外發(fā)現(xiàn)

1. int? 比 int 要占用更多的內(nèi)存

如果你的內(nèi)存數(shù)據(jù)量特別大的話,你就要當(dāng)心了,int? 比 int 在x64上要多占4個字節(jié),也就是多一倍,無論線程棧還是托管堆。

2. 為什么bool要占用 4字節(jié)空間?

<1> 線程棧上的演示

肯定有人比較疑惑,bool在C#中不就是一個字節(jié)嘛?你怎么說是4個字節(jié)呢?你要是問我,我只能說從windbg上看就是這樣的,x64系統(tǒng)的線程棧上就是以4個字節(jié)為一個單位,你不信的話,我就在代碼中定義不同字段的 值類型,你看看在線程棧上的分布不就好啦,以事實說話。

byte b1 = byte.MaxValue;byte b2 = byte.MaxValue;short b3 = short.MaxValue;short b4 = short.MaxValue;int b5 = int.MaxValue;int b6 = int.MaxValue;0:000> !clrstack -l OS Thread Id: 0xa98 (0) ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 25]LOCALS:0x000000a8395fedbc = 0x00000000000000ff0x000000a8395fedb8 = 0x00000000000000ff0x000000a8395fedb4 = 0x0000000000007fff0x000000a8395fedb0 = 0x0000000000007fff0x000000a8395fedac = 0x000000007fffffff0x000000a8395feda8 = 0x000000007fffffff

然后把最小的地址0x000000a8395feda8?轉(zhuǎn)儲出來。

0:000> dd 0x000000a8395feda8 000000a8`395feda8 7fffffff 7fffffff 00007fff 00007fff 000000a8`395fedb8 000000ff 000000ff 395feec8 000000a8 000000a8`395fedc8 395fefc8 000000a8 395fee00 000000a8 000000a8`395fedd8 d5b66c93 00007ffc 98e72d30 000001ee 000000a8`395fede8 76504140 00007ffc 00000000 00000000 000000a8`395fedf8 00000000 00007ffc 395feef0 000000a8 000000a8`395fee08 971d0b20 000001ee 00000000 00000000 000000a8`395fee18 d5b66b79 00007ffc 00000000 00000000

對比一下可以看到上面的?7fffffff, 00007fff,000000ff就是相應(yīng)的int,short,byte的MaxValue, 都是占用4個字節(jié)的空間,沒問題吧。

<2> 托管堆演示

var arr1 = new int[] { 10 };var arr2 = new int?[] { 14 };0:000> !clrstack -l OS Thread Id: 0x23f8 (0) 000000859a1fec60 00007ffc76630967 ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 32]LOCALS:0x000000859a1feca0 = 0x000002773cb32d700x000000859a1fec98 = 0x000002773cb32d90000000859a1feeb8 00007ffcd5b66c93 [GCFrame: 000000859a1feeb8] 0:000> !do 0x000002773cb32d70 Name: System.Int32[] MethodTable: 00007ffcd2d58538 EEClass: 00007ffcd2ec5918 Size: 28(0x1c) bytes Array: Rank 1, Number of elements 1, Type Int32 (Print Array) Fields: None 0:000> !do 0x000002773cb32d90 Name: System.Nullable`1[[System.Int32, mscorlib]][] MethodTable: 00007ffcd3fb2058 EEClass: 00007ffcd30221a0 Size: 32(0x20) bytes Array: Rank 1, Number of elements 1, Type VALUETYPE (Print Array) Fields: None0:000> !objsize 0x000002773cb32d70 sizeof(000002773cb32d70) = 32 (0x20) bytes (System.Int32[]) 0:000> !objsize 0x000002773cb32d90 sizeof(000002773cb32d90) = 32 (0x20) bytes (System.Nullable`1[[System.Int32, mscorlib]][])

可以看到,一個是28byte,一個是32byte,多出來的就是那個hasValue哈,有一點要注意了,用!objsize打出來都是32byte,這是因為28byte要靠8對齊就變成32byte啦, 然后我把兩個值類型轉(zhuǎn)儲出來,如下圖:

四:總結(jié)

挖到這里,不知道可挖到了面試官的盲區(qū)啦????,總之int?就是?Nullable<T>, 而且可空比非可空多4個字節(jié)的空間,最后大家要看自己情況使用啦。

總結(jié)

以上是生活随笔為你收集整理的还不明白可空类型原理? 我可要挖到底了的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。