如何在C#中使用指针

2020-06-01 18:01:13王冬梅

一:背景

1. 讲故事

高级语言玩多了,可能很多人对指针或者汇编都淡忘了,本篇就和大家聊一聊指针,虽然C#中是不提倡使用的,但你能说指针在C#中不重要吗?你要知道FCL内库中大量的使用指针,如String,Encoding,FileStream等等数不胜数,如例代码:

	private unsafe static bool EqualsHelper(string strA, string strB)
	{
		fixed (char* ptr = &strA.m_firstChar)
		{
			fixed (char* ptr3 = &strB.m_firstChar)
			{
				char* ptr2 = ptr;
				char* ptr4 = ptr3;
				while (num >= 12) {...}
				while (num > 0 && *(int*)ptr2 == *(int*)ptr4) {...}
			}
		}
	}

	public unsafe Mutex(bool initiallyOwned, string name, out bool createdNew, MutexSecurity mutexSecurity)
	{
		byte* ptr = stackalloc byte[(int)checked(unchecked((ulong)(uint)securityDescriptorBinaryForm.Length))]
	}
 
 private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, out int hr)
 {
  fixed (byte* ptr = bytes)
		{
			num = ((!_isAsync) ? Win32Native.ReadFile(handle, ptr + offset, count, out numBytesRead, IntPtr.Zero) : Win32Native.ReadFile(handle, ptr + offset, count, IntPtr.Zero, overlapped));
		}
 } 

对,你觉得的美好世界,其实都是别人帮你负重前行,退一步说,指针的理解和不理解,对你研究底层源码影响是不能忽视的,指针相对比较抽象,考的是你的空间想象能力,可能现存的不少程序员还是不太明白,因为你缺乏所见即所得的工具,希望这一篇能帮你少走些弯路。

二:windbg助你理解

指针虽然比较抽象,但如果用windbg实时查看内存布局,就很容易帮你理解指针的套路,下面先理解下指针的一些简单概念。

1. &、* 运算符

&取址运算符,用于获取某一个变量的内存地址, *运算符,用于获取指针变量中存储地址指向的值,很抽象吧,看windbg。

 unsafe
 {
  int num1 = 10;
  int* ptr = &num1;
  int** ptr2 = &ptr;
  var num2 = **ptr2;
 }


0:000> !clrstack -l
ConsoleApp4.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp4Program.cs @ 26]
 LOCALS:
  0x000000305f5fef24 = 0x000000000000000a
  0x000000305f5fef18 = 0x000000305f5fef24
  0x000000305f5fef10 = 0x000000305f5fef18
  0x000000305f5fef0c = 0x000000000000000a

2. **运算符

** 也叫二级指针,指向一级指针变量地址的指针,有点意思,如下程序:ptr2指向的就是 ptr的栈上地址, 一图胜千言。

 unsafe
 {
  int num1 = 10;
  int* ptr = &num1;
  int** ptr2 = &ptr;
  var num2 = **ptr2;
 }


0:000> !clrstack -l
ConsoleApp4.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp4Program.cs @ 26]
 LOCALS:
  0x000000305f5fef24 = 0x000000000000000a
  0x000000305f5fef18 = 0x000000305f5fef24
  0x000000305f5fef10 = 0x000000305f5fef18
  0x000000305f5fef0c = 0x000000000000000a