浙江省院士专家工作站建设网站,安卓网页制作软件,浏览器网站在线进入,免费收录软文网站不安全代码和指针#xff08;C# 编程指南#xff09;为了保持类型安全#xff0c;默认情况下#xff0c;C# 不支持指针运算。不过#xff0c;通过使用 unsafe 关键字#xff0c;可以定义可使用指针的不安全上下文。有关指针的更多信息#xff0c;请参见主题指针类型。 注… 不安全代码和指针C# 编程指南 为了保持类型安全默认情况下C# 不支持指针运算。不过通过使用 unsafe 关键字可以定义可使用指针的不安全上下文。有关指针的更多信息请参见主题指针类型。 注意 在公共语言运行库 (CLR) 中不安全代码是指无法验证的代码。C# 中的不安全代码不一定是危险的只是其安全性无法由 CLR 进行验证的代码。因此CLR 只对在完全受信任的程序集中的不安全代码执行操作。如果使用不安全代码由您负责确保您的代码不会引起安全风险或指针错误。有关更多信息请参见安全性与 C#。不安全代码概述 不安全代码具有下列属性 · 方法、类型和可被定义为不安全的代码块。 · 在某些情况下通过移除数组界限检查不安全代码可提高应用程序的性能。 · 当调用需要指针的本机函数时需要使用不安全代码。 · 使用不安全代码将引起安全风险和稳定性风险。 · 在 C# 中为了编译不安全代码必须用 /unsafe 编译应用程序。 指针类型C# 编程指南 在不安全的上下文中类型可以是指针类型以及值类型或引用类型。指针类型声明具有下列形式之一 复制 type* identifier; void* identifier; //allowed but not recommended 下列类型都可以是指针类型 · sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal 或 bool。 · 任何枚举类型。 · 任何指针类型。 · 仅包含非托管类型的字段的任何用户定义的结构类型。 指针类型不继承 object并且指针类型与 object 之间不存在转换。此外装箱和取消装箱不支持指针。但是允许在不同指针类型之间以及指针类型与整型之间进行转换。 当在同一个声明中声明多个指针时* 仅与基础类型一起使用而不是作为每个指针名称的前缀。例如 复制 int* p1, p2, p3; // Ok int *p1, *p2, *p3; // Invalid in C# 指针不能指向引用或包含引用的结构因为即使有指针指向对象引用该对象引用也可能会被执行垃圾回收。GC 并不注意是否有任何类型的指针指向对象。 myType* 类型的指针变量的值是 myType 类型的变量的地址。下面是指针类型声明的示例 示例 说明 int* p p 是指向整数的指针 int** p p 是指向整数的指针的指针 int*[] p p 是指向整数的指针的一维数组 char* p p 是指向字符的指针 void* p p 是指向未知类型的指针指针间接寻址运算符 * 可用于访问位于指针变量所指向的位置的内容。例如对于下面的声明 复制 int* myVariable; 表达式 *myVariable 表示在 myVariable 中包含的地址处找到的 int 变量。 不能对 void* 类型的指针应用间接寻址运算符。但是可以使用强制转换将 void 指针转换为其他指针类型反之亦然。 指针可以为 null。如果将间接寻址运算符应用于 null 指针则会导致由实现定义的行为。 注意在方法之间传递指针会导致未定义的行为。示例包括通过 Out 或 Ref 参数向局部变量返回指针或作为函数结果向局部变量返回指针。如果将指针设置在固定的块中它所指向的变量可能不再是固定的。 下表列出可在不安全的上下文中针对指针执行的运算符和语句 运算符/语句 用途 * 执行指针间接寻址。 - 通过指针访问结构的成员。 [] 对指针建立索引。 获取变量的地址。 和 -- 递增或递减指针。 加、减 执行指针算法。 、!、、、 和 比较指针。 stackalloc 在堆栈上分配内存。 fixed 语句 临时固定变量以便可以找到其地址。 固定大小的缓冲区C# 编程指南 在 C# 2.0 中可以使用 fixed 语句在数据结构中创建固定大小的数组。使用现有代码如使用其他语言、预先存在的 DLL 或 COM 项目编写的代码时这种方法非常有用。固定数组可采用允许普通结构成员使用的任何属性或修饰符。唯一的限制是数组类型必须是 bool、byte、 char、 short、int、long、sbyte、ushort、uint、ulong、float 或 double。 复制 private fixed char name[30]; 备注 在以前版本的 C# 中声明 C 样式的固定大小结构是很困难的因为包含数组的 C# 结构不包含数组元素而是包含对元素的引用。 C# 2.0 添加了在结构当用在不安全的代码块中时中嵌入固定大小的数组的功能。 例如在 C# 2.0 之前下面的 struct 的大小为 8 字节其中 pathName 数组是对堆分配的数组的引用 复制 public struct MyArray { public char[] pathName; private int reserved; } 在 C# 2.0 中struct 可使用嵌入数组进行声明 复制 public struct MyArray // This code must appear in an unsafe block { public fixed char pathName[128]; } 在此结构中pathName 数组具有固定的大小和位置因此可用在其他不安全的代码中。 128 个元素的 char 数组的大小为 256 字节。在固定大小的 char 缓冲区中每个字符始终占用两个字节而与编码无关。即使将 char 缓冲区封送到具有 CharSet CharSet.Auto 或 CharSet CharSet.Ansi 的 API 方法或结构也是如此。有关更多信息请参见 CharSet。 另一种常见的固定大小的数组是 bool 数组。bool 数组中的元素的大小始终为一个字节。bool 数组不适合用于创建位数组或位缓冲区。 注意 除了用 stackalloc 创建的内存C# 编译器和公共语言运行库 (CLR) 不执行任何安全缓冲区溢出检查。与所有不安全代码一样请谨慎使用。不安全缓冲区与常规数组有如下差异 · 不安全缓冲区只能用在不安全上下文中。 · 不安全缓冲区始终是向量或一维数组。 · 数组的声明应包括计数如 char id[8]。而不能使用 char id[]。 · 不安全缓冲区只能是不安全上下文中的结构的实例字段。 如何使用指针复制字节数组C# 编程指南 下面的示例使用指针将字节从一个数组复制到另一个使用指针的数组。 此示例使用 unsafe 关键字它允许在 Copy 方法内使用指针。fixed 语句用于声明指向源数组和目标数组的指针。这将锁定源数组和目标数组在内存中的位置使其不会因为垃圾回收操作而移动。这些内存块将在 fixed 块结束时取消锁定。因为本示例中 Copy 函数使用了 unsafe 关键字它必须使用 /unsafe 编译器选项进行编译。 示例 复制 // compile with: /unsafe 复制 class TestCopy { // The unsafe keyword allows pointers to be used within the following method: static unsafe void Copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int count) { if (src null || srcIndex 0 || dst null || dstIndex 0 || count 0) { throw new System.ArgumentException(); } int srcLen src.Length; int dstLen dst.Length; if (srcLen - srcIndex count || dstLen - dstIndex count) { throw new System.ArgumentException(); } // The following fixed statement pins the location of the src and dst objects // in memory so that they will not be moved by garbage collection. fixed (byte* pSrc src, pDst dst) { byte* ps pSrc; byte* pd pDst; // Loop over the count in blocks of 4 bytes, copying an integer (4 bytes) at a time: for (int i 0 ; i count / 4 ; i) { *((int*)pd) *((int*)ps); pd 4; ps 4; } // Complete the copy by moving any bytes that werent moved in blocks of 4: for (int i 0; i count % 4 ; i) { *pd *ps; pd; ps; } } } static void Main() { byte[] a new byte[100]; byte[] b new byte[100]; for (int i 0; i 100; i) { a[i] (byte)i; } Copy(a, 0, b, 0, 100); System.Console.WriteLine(The first 10 elements are:); for (int i 0; i 10; i) { System.Console.Write(b[i] ); } System.Console.WriteLine(\n); } } 输出 The first 10 elements are: 0 1 2 3 4 5 6 7 8 9 如何使用 Windows ReadFile 函数C# 编程指南 下面的示例通过读取并显示一个文本文件来演示 Windows ReadFile 函数。ReadFile 函数需要使用 unsafe 代码因为它需要一个作为参数的指针。 传递到 Read 函数的字节数组是托管类型。这意味着公共语言运行库 (CLR) 垃圾回收器可能会随意地对数组使用的内存进行重新定位。为了防止出现这种情况使用 fixed 来获取指向内存的指针并对它进行标记以便垃圾回收器不会移动它。在 fixed 块的末尾内存将自动返回以便能够通过垃圾回收移动。 此功能称为“声明式锁定”。锁定的好处是系统开销非常小除非在 fixed 块中发生垃圾回收但此情况不太可能发生。 示例 复制 class FileReader { const uint GENERIC_READ 0x80000000; const uint OPEN_EXISTING 3; System.IntPtr handle; [System.Runtime.InteropServices.DllImport(kernel32, SetLastError true)] static extern unsafe System.IntPtr CreateFile ( string FileName, // file name uint DesiredAccess, // access mode uint ShareMode, // share mode uint SecurityAttributes, // Security Attributes uint CreationDisposition, // how to create uint FlagsAndAttributes, // file attributes int hTemplateFile // handle to template file ); [System.Runtime.InteropServices.DllImport(kernel32, SetLastError true)] static extern unsafe bool ReadFile ( System.IntPtr hFile, // handle to file void* pBuffer, // data buffer int NumberOfBytesToRead, // number of bytes to read int* pNumberOfBytesRead, // number of bytes read int Overlapped // overlapped buffer ); [System.Runtime.InteropServices.DllImport(kernel32, SetLastError true)] static extern unsafe bool CloseHandle ( System.IntPtr hObject // handle to object ); public bool Open(string FileName) { // open the existing file for reading handle CreateFile ( FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0 ); if (handle ! System.IntPtr.Zero) { return true; } else { return false; } } public unsafe int Read(byte[] buffer, int index, int count) { int n 0; fixed (byte* p buffer) { if (!ReadFile(handle, p index, count, n, 0)) { return 0; } } return n; } public bool Close() { return CloseHandle(handle); } } class Test { static int Main(string[] args) { if (args.Length ! 1) { System.Console.WriteLine(Usage : ReadFile FileName); return 1; } if (!System.IO.File.Exists(args[0])) { System.Console.WriteLine(File args[0] not found.); return 1; } byte[] buffer new byte[128]; FileReader fr new FileReader(); if (fr.Open(args[0])) { // Assume that an ASCII file is being read. System.Text.ASCIIEncoding Encoding new System.Text.ASCIIEncoding(); int bytesRead; do { bytesRead fr.Read(buffer, 0, buffer.Length); string content Encoding.GetString(buffer, 0, bytesRead); System.Console.Write({0}, content); } while (bytesRead 0); fr.Close(); return 0; } else { System.Console.WriteLine(Failed to open requested file); return 1; } } } unsafeC# 参考 unsafe 关键字表示不安全上下文该上下文是任何涉及指针的操作所必需的。有关更多信息请参见不安全代码和指针C# 编程指南。 可以在类型或成员的声明中使用 unsafe 修饰符。因此类型或成员的整个正文范围均被视为不安全上下文。例如以下是用 unsafe 修饰符声明的方法 复制 unsafe static void FastCopy(byte[] src, byte[] dst, int count) { // Unsafe context: can use pointers here. } 不安全上下文的范围从参数列表扩展到方法的结尾因此指针在以下参数列表中也可以使用 复制 unsafe static void FastCopy ( byte* ps, byte* pd, int count ) {...} 还可以使用不安全块从而能够使用该块内的不安全代码。例如 复制 unsafe { // Unsafe context: can use pointers here. } 若要编译不安全代码必须指定 /unsafe 编译器选项。无法通过公共语言运行库验证不安全代码。 示例 复制 // cs_unsafe_keyword.cs // compile with: /unsafe using System; class UnsafeTest { // Unsafe method: takes pointer to int: unsafe static void SquarePtrParam(int* p) { *p * *p; } unsafe static void Main() { int i 5; // Unsafe method: uses address-of operator (): SquarePtrParam(i); Console.WriteLine(i); } } 输出 25 fixed 语句C# 参考 fixed 语句禁止垃圾回收器重定位可移动的变量。fixed 语句只能出现在不安全的上下文中。Fixed 还可用于创建固定大小的缓冲区。 备注 fixed 语句设置指向托管变量的指针并在 statement 执行期间“钉住”该变量。如果没有 fixed 语句则指向可移动托管变量的指针的作用很小因为垃圾回收可能不可预知地重定位变量。C# 编译器只允许在 fixed 语句中分配指向托管变量的指针。 复制 // assume class Point { public int x, y; } // pt is a managed variable, subject to garbage collection. Point pt new Point(); // Using fixed allows the address of pt members to be // taken, and pins pt so it isnt relocated. fixed ( int* p pt.x ) { *p 1; } 可以用数组或字符串的地址初始化指针 复制 fixed (int* p arr) ... // equivalent to p arr[0] fixed (char* p str) ... // equivalent to p str[0] 只要指针的类型相同就可以初始化多个指针 复制 fixed (byte* ps srcarray, pd dstarray) {...} 要初始化不同类型的指针只需嵌套 fixed 语句 复制 fixed (int* p1 p.x) { fixed (double* p2 array[5]) { // Do something with p1 and p2. } } 执行完语句中的代码后任何固定变量都被解除固定并受垃圾回收的制约。因此不要指向 fixed 语句之外的那些变量。 在不安全模式中可以在堆栈上分配内存。堆栈不受垃圾回收的制约因此不需要被锁定。有关更多信息请参见 stackalloc。 示例 复制 // statements_fixed.cs // compile with: /unsafe using System; class Point { public int x, y; } class FixedTest { // Unsafe method: takes a pointer to an int. unsafe static void SquarePtrParam (int* p) { *p * *p; } unsafe static void Main() { Point pt new Point(); pt.x 5; pt.y 6; // Pin pt in place: fixed (int* p pt.x) { SquarePtrParam (p); } // pt now unpinned Console.WriteLine ({0} {1}, pt.x, pt.y); } } 输出 25 6 stackallocC# 参考 在不安全的代码上下文中使用可以在堆栈上分配内存块。 复制 int* fib stackalloc int[100]; 备注 上面的示例在堆栈而不是堆上分配了一个内存块它的大小足以包含 100 个 int 类型的元素该块的地址存储在 fib 指针中。此内存不受垃圾回收的制约因此不必将其钉住通过 fixed。内存块的生存期受定义它的方法的生存期的限制没有在方法返回之前释放内存的途径。 stackalloc 仅在局部变量的初始值设定项中有效。 由于涉及指针类型stackalloc 要求不安全上下文。请参见不安全代码和指针。 stackalloc 类似于 C 运行时库中的 _alloca。 安全性 不安全代码是天生比非不安全替代代码安全性更低的代码。但是通过使用 stackalloc 可以自动启用公共语言运行库 (CLR) 中的缓冲区溢出检测功能。如果检测到缓冲区溢出进程将尽快终止以最大限度地减小执行恶意代码的机会。 示例 复制 // cs_keyword_stackalloc.cs // compile with: /unsafe using System; class Test { static unsafe void Main() { int* fib stackalloc int[100]; int* p fib; *p *p 1; for (int i 2; i 100; i, p) { *p p[-1] p[-2]; } for (int i 0; i 10; i) { Console.WriteLine(fib[i]); } } } 输出 1 1 2 3 5 8 13 21 34 55 转载于:https://www.cnblogs.com/jxnclyk/archive/2010/05/28/1746132.html