#unity/日常积累 # IntPtr 结构 - 参考 反馈 [](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0#definition) ## 定义 命名空间: [System](https://learn.microsoft.com/zh-cn/dotnet/api/system?view=net-8.0) 程序集: System.Runtime.dll Source: [IntPtr.cs](https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs) 表示一个带符号整数,其中位宽度与指针相同。 ## 示例 以下示例使用托管指针来反转数组中的字符。 初始化 对象 [String](https://learn.microsoft.com/zh-cn/dotnet/api/system.string?view=net-8.0) 并获取其长度后,它会执行以下操作: 1. 调用 方法, [Marshal.StringToHGlobalAnsi](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.stringtohglobalansi?view=net-8.0) 将 Unicode 字符串作为 ANSI (单字节) 字符复制到非托管内存。 方法返回一个 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 对象,该对象指向非托管字符串的开头。 Visual Basic 示例直接使用此指针;在 C++、F# 和 C# 示例中,它被强制转换为指向字节的指针。 2. [Marshal.AllocHGlobal](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.allochglobal?view=net-8.0)调用 方法以分配与非托管字符串占用的相同字节数。 方法返回一个 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 对象,该对象指向非托管内存块的开头。 Visual Basic 示例直接使用此指针;在 C++、F# 和 C# 示例中,它被强制转换为指向字节的指针。 3. Visual Basic 示例定义一个名为 `offset` 的变量,该变量等于 ANSI 字符串的长度。 它用于确定将 ANSI 字符串中的下一个字符复制到的非托管内存中的偏移量。 由于其起始值为字符串的长度,因此复制操作会将字符从字符串的开头复制到内存块的末尾。 C#、F# 和 C++ 示例调用 [ToPointer](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr.topointer?view=net-8.0) 方法以获取指向字符串的起始地址和非托管内存块的非托管指针,并将小于字符串长度的指针添加到 ANSI 字符串的起始地址。 由于非托管字符串指针现在指向字符串的末尾,因此复制操作会将字符串末尾的字符复制到内存块的开头。 4. 使用循环将字符串中的每个字符复制到非托管内存块。 Visual Basic 示例调用 [Marshal.ReadByte(IntPtr, Int32)](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.readbyte?view=net-8.0#system-runtime-interopservices-marshal-readbyte(system-intptr-system-int32)) 方法,以从指向 ANSI 字符串的托管指针的指定偏移量读取字节 (或单字节字符) 。 偏移量随循环的每次迭代而递增。 然后, [Marshal.WriteByte(IntPtr, Int32, Byte)](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.writebyte?view=net-8.0#system-runtime-interopservices-marshal-writebyte(system-intptr-system-int32-system-byte)) 它调用 方法,将字节写入由非托管内存块的起始地址和 `offset`定义的内存地址。 然后,它会递 `offset`减 。 C#、F# 和 C++ 示例执行复制操作,然后递减指向非托管 ANSI 字符串中下一个位置的地址的指针,并将指针递增到非托管块中的下一个地址。 5. 所有示例都调用 [Marshal.PtrToStringAnsi](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.ptrtostringansi?view=net-8.0) ,将包含复制的 ANSI 字符串的非托管内存块转换为托管的 Unicode [String](https://learn.microsoft.com/zh-cn/dotnet/api/system.string?view=net-8.0) 对象。 6. 在显示原始字符串和反向字符串后,所有示例都会调用 [FreeHGlobal](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.freehglobal?view=net-8.0) 方法来释放为非托管 ANSI 字符串和非托管内存块分配的内存。 ``` cs using System; using System.Runtime.InteropServices; class NotTooSafeStringReverse { static public void Main() { string stringA = "I seem to be turned around!"; int copylen = stringA.Length; // Allocate HGlobal memory for source and destination strings IntPtr sptr = Marshal.StringToHGlobalAnsi(stringA); IntPtr dptr = Marshal.AllocHGlobal(copylen + 1); // The unsafe section where byte pointers are used. unsafe { byte *src = (byte *)sptr.ToPointer(); byte *dst = (byte *)dptr.ToPointer(); if (copylen > 0) { // set the source pointer to the end of the string // to do a reverse copy. src += copylen - 1; while (copylen-- > 0) { *dst++ = *src--; } *dst = 0; } } string stringB = Marshal.PtrToStringAnsi(dptr); Console.WriteLine("Original:\n{0}\n", stringA); Console.WriteLine("Reversed:\n{0}", stringB); // Free HGlobal memory Marshal.FreeHGlobal(dptr); Marshal.FreeHGlobal(sptr); } } // The progam has the following output: // // Original: // I seem to be turned around! // // Reversed: // !dnuora denrut eb ot mees I ``` ## 注解 类型 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 设计为一个整数,其大小与指针相同。 也就是说,此类型的实例应在 32 位进程中为 32 位,在 64 位进程中为 64 位。 支持 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 指针的语言可以使用 类型,并作为在支持和不支持指针的语言之间引用数据的常用方法。 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 对象还可用于保存句柄。 例如, 的 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 实例在 类中 [System.IO.FileStream](https://learn.microsoft.com/zh-cn/dotnet/api/system.io.filestream?view=net-8.0) 广泛使用,用于保存文件句柄。  备注 使用 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 作为指针或句柄容易出错且不安全。 它只是一个整数类型,由于大小相同,可用作指针和句柄的交换格式。 在特定的交换要求(例如,将数据传递给不支持指针的语言)之外,应使用正确类型的指针来表示指针,并 [SafeHandle](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.safehandle?view=net-8.0) 应用于表示句柄。 此类型实现 [ISerializable](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.iserializable?view=net-8.0)。 在 .NET 5 及更高版本中,此类型还实现 [IFormattable](https://learn.microsoft.com/zh-cn/dotnet/api/system.iformattable?view=net-8.0) 接口。 在 .NET 7 及更高版本中,此类型还实现 [IBinaryInteger](https://learn.microsoft.com/zh-cn/dotnet/api/system.numerics.ibinaryinteger-1?view=net-8.0)、 [IMinMaxValue](https://learn.microsoft.com/zh-cn/dotnet/api/system.numerics.iminmaxvalue-1?view=net-8.0)和 [ISignedNumber](https://learn.microsoft.com/zh-cn/dotnet/api/system.numerics.isignednumber-1?view=net-8.0) 接口。 在从版本 9.0 开始的 C# 中,可以使用内置 `nint` 类型定义本机大小的整数。 此类型在内部由 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 类型表示,并提供适用于整数类型的操作和转换。 有关详细信息,请参阅 [nint 和 nuint 类型](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/nint-nuint)。 在 C# 中,从版本 11 开始,以 .NET 7 或更高版本运行时为目标时, `nint` 是 的 [IntPtr](https://learn.microsoft.com/zh-cn/dotnet/api/system.intptr?view=net-8.0) 别名,其别名与 `int` 的别名 [Int32](https://learn.microsoft.com/zh-cn/dotnet/api/system.int32?view=net-8.0)相同。