2025-03-26 00:02:56 +08:00

119 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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)相同。