海淀营销型网站建设,酷炫的网站模板免费下载,企业seo培训,php中英双语农业公司网站源码一#xff1a;背景1. 讲故事做好自媒体到现在有一个月了#xff0c;关注我的兄弟应该知道我产出了不少文章#xff0c;号里的粉丝也多起来了#xff0c;我也尽最大努力做到有问必回#xff0c;现在是基础的、高深的问题都接踵而来#xff0c;可我也只是一只小菜鸟#x… 一背景1. 讲故事做好自媒体到现在有一个月了关注我的兄弟应该知道我产出了不少文章号里的粉丝也多起来了我也尽最大努力做到有问必回现在是基础的、高深的问题都接踵而来可我也只是一只小菜鸟想飞也飞不动了(┬┬)昨天号里有位朋友被面试官问到可空类型的原理回答的不好面试官也是面就面呗又给不了多少银子还动不动就原理哪有那么多原理搞得双方都尴尬????????????。二给我锄头我要挖到底这种问题要怎么挖呢我在之前的文章也聊过C#代码到机器码中间有两个编译过程一个是csc编译后的IL代码一个是jit编译后的native代码所以搞懂IL代码和native代码就是我们要深究的方向我还是把那篇文章的图拿过来。为了方便演示我就定义一个int?类型接收非null和null两种情况。static void Main(string[] args){int? num1 10;int? num2 null;Console.WriteLine(执行结束啦);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.Nullable1int32 num1,[1] valuetype [mscorlib]System.Nullable1int32 num2)IL_0000: nopIL_0001: ldloca.s 0IL_0003: ldc.i4.s 10IL_0005: call instance void valuetype [mscorlib]System.Nullable1int32::.ctor(!0)IL_000a: ldloca.s 1IL_000c: initobj valuetype [mscorlib]System.Nullable1int32IL_0012: ldstr 执行结束啦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.Nullableint32 然后从valuetype 标记可以看到这玩意是个值类型所以把上面的代码回转成C#代码就是下面这样。{static void Main(string[] args){//int? num1 10;//int? num2 null;Nullableint num3 new Nullableint(10);Nullableint num4 new Nullableint();Console.WriteLine(执行结束啦);Console.ReadLine();}
很简单吧那怎么输出num3和num4呢直接Console.WriteLine就好了。这里你肯定有一个疑问为什么num3输出10而num4什么都没输出呢哈哈这是因为Nullable的ToString()被重写了再来看下ToString被重写成啥样了代码如下public struct NullableT 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方法里要么返回空字符串要么返回你在构造函数中塞入的value这这么简单IL代码挖到这里就可以了。2. 挖机器代码要看num1和num2的机器代码其实也就是看 NullableT 的内存布局方式这里我使用windbg还是使用 !clrstack -l 查看线程栈。int? num1 10;int? num2 null;0:007 ~0s
ntdll!ZwReadFile0x14:
00007ffcec11aa64 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的线程栈上存放的内容分别是0x0000000a00000001和 0x0000000000000000, 不过这值也挺奇怪的一个是1一个是0。。。我们用 dd 命令把地址转储出来。
0:000 dd 0x00000018a9dfeaf8
00000018a9dfeaf8 00000001 0000000a a9dfec08 00000018
0:000 dd 0x00000018a9dfeaf0
00000018a9dfeaf0 00000000 00000000 00000001 0000000a
在num1的内存区域中有一个十六进制值 0000000a 这就是十进制的10那前面的 00000001 是什么东西呢大家不要忘啦, int?是语法糖, 你现在看的是 NullableT 哈。。。看清楚啦这个结构体里面有两个值类型字段自然 00000001 就是 hasValuetrue啦。 num2也就好理解了,两个默认值也就是两个0了。00000000 00000000。三有意外发现1. int? 比 int 要占用更多的内存如果你的内存数据量特别大的话你就要当心了int? 比 int 在x64上要多占4个字节也就是多一倍无论线程栈还是托管堆。2. 为什么bool要占用 4字节空间1 线程栈上的演示肯定有人比较疑惑bool在C#中不就是一个字节嘛你怎么说是4个字节呢你要是问我我只能说从windbg上看就是这样的x64系统的线程栈上就是以4个字节为一个单位你不信的话我就在代码中定义不同字段的 值类型你看看在线程栈上的分布不就好啦以事实说话。 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 转储出来。
0:000 dd 0x000000a8395feda8
000000a8395feda8 7fffffff 7fffffff 00007fff 00007fff
000000a8395fedb8 000000ff 000000ff 395feec8 000000a8
000000a8395fedc8 395fefc8 000000a8 395fee00 000000a8
000000a8395fedd8 d5b66c93 00007ffc 98e72d30 000001ee
000000a8395fede8 76504140 00007ffc 00000000 00000000
000000a8395fedf8 00000000 00007ffc 395feef0 000000a8
000000a8395fee08 971d0b20 000001ee 00000000 00000000
000000a8395fee18 d5b66b79 00007ffc 00000000 00000000
对比一下可以看到上面的 7fffffff, 00007fff,000000ff就是相应的int,short,byte的MaxValue, 都是占用4个字节的空间没问题吧。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.Nullable1[[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.Nullable1[[System.Int32, mscorlib]][])
可以看到一个是28byte一个是32byte多出来的就是那个hasValue哈有一点要注意了用!objsize打出来都是32byte这是因为28byte要靠8对齐就变成32byte啦 然后我把两个值类型转储出来如下图四总结挖到这里不知道可挖到了面试官的盲区啦????总之int?就是 NullableT 而且可空比非可空多4个字节的空间最后大家要看自己情况使用啦。