119 lines
8.2 KiB
Markdown
119 lines
8.2 KiB
Markdown
#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<TSelf>](https://learn.microsoft.com/zh-cn/dotnet/api/system.numerics.ibinaryinteger-1?view=net-8.0)、 [IMinMaxValue<TSelf>](https://learn.microsoft.com/zh-cn/dotnet/api/system.numerics.iminmaxvalue-1?view=net-8.0)和 [ISignedNumber<TSelf>](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)相同。 |