C领域有哪些前沿问题值得深入探究?

2026-04-27 12:542阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1229个文字,预计阅读时间需要5分钟。

C领域有哪些前沿问题值得深入探究?

目录:

1.举例说明

2.异常原理分析

3.总结

4.举例说明

csharp

namespace ConsoleApp2{ internal class Program { internal static Person person=null;

internal static void Main(string[] args) { try { var age=person?.age; Console.WriteLine(age); } catch (Exception ex) { Console.WriteLine(发生错误: + ex.Message); } } }

internal class Person { public int age; }}

目录
  • 一:举例说明
  • 二:异常原理分析
  • 三:总结

一:举例说明

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是一个 null 对象,很显然这段代码会抛异常,那为什么会抛异常呢?要想找原因,需要从最底层的汇编研究起。

二:异常原理分析

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对象,ecx+4是取 person.age,最后一句就是将 age 放在ebp-3Ch栈位置上,接下来我们来看下 null 时的 ecx 到底是多少,截图如下:

从图中可以看到,此时的ecx=0000000,如果大家了解 windows 的虚拟内存布局,应该知道在虚拟内存的0~0x0000ffff范围内是属于 null 禁入区,凡是落在这个区一概属访问违例,画个图就像下面这样。

到这里原理就搞清楚了,因为 [ecx+4] = [4] 是落在这个 null 区所致,但是。。。。大家有没有发现一个问题,对,就是这里的[ecx+4],因为这里有一个+4偏移来取 age 字段,那我能不能在 person 中多定义一些字段,然后取最后一个字段从而从null 区冲出去。。。哈哈。

2. 真的可以冲出 null 区吗

有了这个想法之后,我决定在Person类中定义 10w 个 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 生成完毕"); } } }

代码执行后,Person.cs就会如期生成,接下来读取person.age99999看看有没有奇迹发生,参考代码如下:

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

我去,万万没想到,把 ClassLoader 给弄崩了。。。。得,那只能改 20000 个 age 试试看吧,参考代码如下:

C领域有哪些前沿问题值得深入探究?

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

接下来我们将断点放在var age = person.age19999;上继续看反汇编代码。

------------- 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

从上面的汇编代码可以看出几点信息。

  • 汇编代码行数多了。
  • ecx+13880h 冲出了 null 区(FFFF) 的边界。

接下来单步调试汇编,发现在cmp dword ptr [ecx],ecx处抛了异常。。。

大家都知道此时的 ecx 的地址是 0 ,从ecx上取内容肯定会抛访问违例,而且这段代码很诡异,一般来说cmp之后都是类似jz,jnz跳转指令,而它仅仅是个半残之句。。。

从这些特征看,这是 JIT 故意在取偏移之前尝试判断ecx是不是 null,动机不纯哈。。。。

三:总结

从这些分析中可以得知,JIT 还是很智能的。

  • 当偏移值落在0~FFFF禁入区内,JIT 就不生成判断代码来减少代码体积。
  • 在偏移值冲出了0~FFFF禁入区,JIT 不得不生成代码来判断。

到此这篇关于探究C#访问null字段会抛异常原因的文章就介绍到这了,更多相关C# null字段异常内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

本文共计1229个文字,预计阅读时间需要5分钟。

C领域有哪些前沿问题值得深入探究?

目录:

1.举例说明

2.异常原理分析

3.总结

4.举例说明

csharp

namespace ConsoleApp2{ internal class Program { internal static Person person=null;

internal static void Main(string[] args) { try { var age=person?.age; Console.WriteLine(age); } catch (Exception ex) { Console.WriteLine(发生错误: + ex.Message); } } }

internal class Person { public int age; }}

目录
  • 一:举例说明
  • 二:异常原理分析
  • 三:总结

一:举例说明

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是一个 null 对象,很显然这段代码会抛异常,那为什么会抛异常呢?要想找原因,需要从最底层的汇编研究起。

二:异常原理分析

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对象,ecx+4是取 person.age,最后一句就是将 age 放在ebp-3Ch栈位置上,接下来我们来看下 null 时的 ecx 到底是多少,截图如下:

从图中可以看到,此时的ecx=0000000,如果大家了解 windows 的虚拟内存布局,应该知道在虚拟内存的0~0x0000ffff范围内是属于 null 禁入区,凡是落在这个区一概属访问违例,画个图就像下面这样。

到这里原理就搞清楚了,因为 [ecx+4] = [4] 是落在这个 null 区所致,但是。。。。大家有没有发现一个问题,对,就是这里的[ecx+4],因为这里有一个+4偏移来取 age 字段,那我能不能在 person 中多定义一些字段,然后取最后一个字段从而从null 区冲出去。。。哈哈。

2. 真的可以冲出 null 区吗

有了这个想法之后,我决定在Person类中定义 10w 个 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 生成完毕"); } } }

代码执行后,Person.cs就会如期生成,接下来读取person.age99999看看有没有奇迹发生,参考代码如下:

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

我去,万万没想到,把 ClassLoader 给弄崩了。。。。得,那只能改 20000 个 age 试试看吧,参考代码如下:

C领域有哪些前沿问题值得深入探究?

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

接下来我们将断点放在var age = person.age19999;上继续看反汇编代码。

------------- 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

从上面的汇编代码可以看出几点信息。

  • 汇编代码行数多了。
  • ecx+13880h 冲出了 null 区(FFFF) 的边界。

接下来单步调试汇编,发现在cmp dword ptr [ecx],ecx处抛了异常。。。

大家都知道此时的 ecx 的地址是 0 ,从ecx上取内容肯定会抛访问违例,而且这段代码很诡异,一般来说cmp之后都是类似jz,jnz跳转指令,而它仅仅是个半残之句。。。

从这些特征看,这是 JIT 故意在取偏移之前尝试判断ecx是不是 null,动机不纯哈。。。。

三:总结

从这些分析中可以得知,JIT 还是很智能的。

  • 当偏移值落在0~FFFF禁入区内,JIT 就不生成判断代码来减少代码体积。
  • 在偏移值冲出了0~FFFF禁入区,JIT 不得不生成代码来判断。

到此这篇关于探究C#访问null字段会抛异常原因的文章就介绍到这了,更多相关C# null字段异常内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!