ImageVerifierCode 换一换
格式:DOCX , 页数:27 ,大小:32.08KB ,
资源ID:5453760      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5453760.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(C#不安全代码.docx)为本站会员(b****3)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

C#不安全代码.docx

1、C#不安全代码18. 不安全代码 31318.1 不安全上下文 31318.2 指针类型 31518.3 固定和可移动变量 31818.4 指针转换 31818.5 表达式中的指针 31918.5.1 指针间接寻址 32018.5.2 指针成员访问 32018.5.3 指针元素访问 32118.5.4 address-of 运算符 32218.5.5 指针递增和递减 32318.5.6 指针算术运算 32318.5.7 指针比较 32418.5.8 sizeof 运算符 32418.6 fixed 语句 32518.7 堆栈分配 32818.8 动态内存分配 3291. 不安全代码如前面几章所

2、定义,核心 C# 语言没有将指针列入它所支持的数据类型,从而与 C 和 C+ 有着显著的区别。作为替代,C# 提供了各种引用类型,并能够创建可由垃圾回收器管理的对象。这种设计结合其他功能,使 C# 成为比 C 或 C+ 安全得多的语言。在核心 C# 语言中,不可能有未初始化的变量、“虚”指针或者超过数组的边界对其进行索引的表达式。这样,以往总是不断地烦扰 C 和 C+ 程序的一系列错误就不会再出现了。尽管实际上对 C 或 C+ 中的每种指针类型构造,C# 都设置了与之对应的引用类型,但仍然会有一些场合需要访问指针类型。例如,当需要与基础操作系统进行交互、访问内存映射设备,或实现一些以时间为关键

3、的算法时,若没有访问指针的手段,就不可能或者至少很难完成。为了满足这样的需求,C# 提供了编写不安全代码 (unsafe code) 的能力。在不安全代码中,可以声明和操作指针,可以在指针和整型之间执行转换,还可以获取变量的地址,等等。在某种意义上,编写不安全代码很像在 C# 程序中编写 C 代码。无论从开发人员还是从用户角度来看,不安全代码事实上都是一种“安全”功能。不安全代码必须用修饰符 unsafe 明确地标记,这样开发人员就不会误用不安全功能,而执行引擎将确保不会在不受信任的环境中执行不安全代码。1.1 不安全上下文C# 的不安全功能仅用于不安全上下文中。不安全上下文是通过在类型或成员

4、的声明中包含一个 unsafe 修饰符或者通过使用 unsafe-statement 引入的: 类、结构、接口或委托的声明可以包含一个 unsafe 修饰符,在这种情况下,该类型声明的整个文本范围(包括类、结构或接口的体)被认为是不安全上下文。 在字段、方法、属性、事件、索引器、运算符、实例构造函数、析构函数或静态构造函数的声明中,也可以包含一个 unsafe 修饰符,在这种情况下,该成员声明的整个文本范围被认为是不安全上下文。 unsafe-statement 使得可以在 block 内使用不安全上下文。该语句关联的 block 的整个文本范围被认为是不安全上下文。下面显示了关联的语法扩展。

5、为简洁起见,用省略号 (.) 表示前几章中出现过的产生式。class-modifier:.unsafestruct-modifier:.unsafeinterface-modifier:.unsafedelegate-modifier:.unsafefield-modifier:.unsafemethod-modifier:.unsafeproperty-modifier:.unsafeevent-modifier:.unsafeindexer-modifier:.unsafeoperator-modifier:.unsafeconstructor-modifier:.unsafedestru

6、ctor-declaration:attributesopt externopt unsafeopt identifier ( ) destructor-bodyattributesopt unsafeopt externopt identifier ( ) destructor-bodystatic-constructor-modifiers:externopt unsafeopt staticunsafeopt externopt staticexternopt static unsafeopt externopt static unsafeopt static externopt uns

7、afeoptstatic unsafeopt externoptembedded-statement:.unsafe-statementunsafe-statement:unsafe block在下面的示例中public unsafe struct Node public int Value; public Node* Left; public Node* Right;在结构声明中指定的 unsafe 修饰符导致该结构声明的整个文本范围成为不安全上下文。因此,可以将 Left 和 Right 字段声明为指针类型。上面的示例还可以编写为public struct Node public int

8、Value; public unsafe Node* Left; public unsafe Node* Right;此处,字段声明中的 unsafe 修饰符导致这些声明被认为是不安全上下文。除了建立不安全上下文从而允许使用指针类型外,unsafe 修饰符对类型或成员没有影响。在下面的示例中public class A public unsafe virtual void F() char* p; . public class B: A public override void F() base.F(); . A 中 F 方法上的 unsafe 修饰符直接导致 F 的文本范围成为不安全上下文并

9、可以在其中使用语言的不安全功能。在 B 中对 F 的重写中,不需要重新指定 unsafe 修饰符,除非 B 中的 F 方法本身需要访问不安全功能。当指针类型是方法签名的一部分时,情况略有不同public unsafe class A public virtual void F(char* p) .public class B: A public unsafe override void F(char* p) .此处,由于 F 的签名包括指针类型,因此它只能出现在不安全上下文中。然而,为设置此不安全上下文,既可以将整个类设置为不安全的(如 A 中的情况),也可以仅在方法声明中包含一个 unsaf

10、e 修饰符(如 B 中的情况)。1.2 指针类型在不安全上下文中,type(第 4 章)可以是 pointer-type,也可以是 value-type 或 reference-type。但是,pointer-type 也能在不安全上下文以外的 typeof 表达式(第 7.5.11 节)中使用,因为此类使用不是不安全的。type:value-typereference-typepointer-typepointer-type 可表示为 unmanaged-type 后跟一个 * 标记,或者关键字 void 后跟一个 * 标记:pointer-type:unmanaged-type *void

11、 *unmanaged-type:type指针类型中,在 * 前面指定的类型称为该指针类型的目标类型 (referent type)。它表示该指针类型的值所指向的变量的类型。与引用(引用类型的值)不同,指针不受垃圾回收器跟踪(垃圾回收器并不知晓指针和它们指向的数据)。出于此原因,不允许指针指向引用或者包含引用的结构,并且指针的目标类型必须是 unmanaged-type。unmanaged-type 是任何不是 reference-type 并且在任何嵌套级别都不包含 reference-type 字段的类型。换句话说,unmanaged-type 是下列类型之一: sbyte、byte、sh

12、ort、ushort、int、uint、long、ulong、char、float、double、decimal 或 bool。 任何 enum-type。 任何 pointer-type。 任何由用户定义的只包含 unmanaged-type 字段的 struct-type。将指针和引用进行混合使用时的基本规则是;引用(对象)的目标可以包含指针,但指针的目标不能包含引用。下表给出了一些指针类型的示例:示例说明byte*指向 byte 的指针char*指向 char 的指针int*指向 int 的指针的指针int*一维数组,它的元素是指向 int 的指针void*指向未知类型的指针对于某个给定

13、实现,所有的指针类型都必须具有相同的大小和表示形式。与 C 和 C+ 不同,在 C# 中,当在同一声明中声明多个指针时,* 只与基础类型写在一起,而不充当每个指针名称的前缀标点符号。例如int* pi, pj; / NOT as int *pi, *pj;类型为 T* 的一个指针的值表示类型为 T 的一个变量的地址。指针间接寻址运算符 * (第 18.5.1 节) 可用于访问此变量。例如,给定int* 类型的变量 P,则表达式 *P 表示 int 变量,该变量的地址就是 P 的值。 与对象引用类似,指针可以是 null。如果将间接寻址运算符应用于 null 指针,则其行为将由实现自己定义。值为

14、 null 的指针表示为将该指针的所有位都置零。void* 类型表示指向未知类型的指针。因为目标类型是未知的,所以间接寻址运算符不能应用于 void* 类型的指针,也不能对这样的指针执行任何算术运算。但是,void* 类型的指针可以强制转换为任何其他指针类型(反之亦然)。指针类型是一个单独类别的类型。与引用类型和值类型不同,指针类型不从 object 继承,而且不存在指针类型和 object 之间的转换。具体而言,指针不支持装箱和拆箱(第 4.3 节)操作。但是,允许在不同指针类型之间以及指针类型与整型之间进行转换。在第 18.4 节中对此进行了描述。pointer-type 可用作易失字段的

15、类型(第 10.4.3 节)。虽然指针可以作为 ref 或 out 参数传递,但这样做可能会导致未定义的行为,例如,指针可能被设置为指向一个局部变量,而当调用方法返回时,该局部变量可能已不存在了;或者指针曾指向一个固定对象,但当调用方法返回时,该对象不再是固定的了。例如:using System;class Test static int value = 20; unsafe static void F(out int* pi1, ref int* pi2) int i = 10; pi1 = &i; fixed (int* pj = &value) / . pi2 = pj; static

16、void Main() int i = 10; unsafe int* px1; int* px2 = &i; F(out px1, ref px2); Console.WriteLine(*px1 = 0, *px2 = 1, *px1, *px2); / undefined behavior 方法可以返回某一类型的值,而该类型可以是指针。例如,给定一个指向连续的 int 值序列的指针、该序列的元素个数,和另外一个 int 值 (value),下面的方法将在该整数序列中查找与该 value 匹配的值,若找到匹配项,则返回该匹配项的地址;否则,它将返回 null:unsafe static i

17、nt* Find(int* pi, int size, int value) for (int i = 0; i 运算符可用于通过指针访问结构的成员(第 18.5.2 节)。 运算符可用于索引指针(第 18.5.3 节)。 & 运算符可用于获取变量的地址(第 18.5.4 节)。 + 和 - 运算符可以用于递增和递减指针(第 18.5.5 节)。 + 和 - 运算符可用于执行指针算术运算(第 18.5.6 节)。 =、!=、 运算符可以用于比较指针(第 18.5.7 节)。 stackalloc 运算符可用于从调用堆栈(第 18.7 节)中分配内存。 fixed 语句可用于临时固定一个变量,以

18、便可以获取它的地址(第 18.6 节)。1.3 固定和可移动变量address-of 运算符(第 18.5.4 节)和 fixed 语句(第 18.6 节)将变量划分为两个类别:固定变量 (fixed variable) 和可移动变量 (moveable variable)。固定变量驻留在不受垃圾回收器的操作影响的存储位置中。(固定变量的示例包括局部变量、值参数和由取消指针引用而创建的变量。)另一方面,可移动变量则驻留在会被垃圾回收器重定位或处置的存储位置中。(可移动变量的示例包括对象中的字段和数组的元素。)& 运算符(第 18.5.4 节)允许不受限制地获取固定变量的地址。但是,由于可移动变

19、量会受到垃圾回收器的重定位或处置,因此可移动变量的地址只能使用 fixed 语句(第 18.6 节)获取,而且该地址只在此 fixed 语句的生存期内有效。准确地说,固定变量是下列之一: 用引用局部变量或值参数的 simple-name(第 7.5.2 节)表示的变量。 用 V.I 形式的 member-access(第 7.5.4 节)表示的变量,其中 V 是 struct-type 的固定变量。 用 *P 形式的 pointer-indirection-expression(第 18.5.1 节)、P-I 形式的 pointer-member-access(第 18.5.2 节)或 PE

20、形式的 pointer-element-access(第 18.5.3 节)表示的变量。所有其他变量都属于可移动变量。请注意静态字段属于可移动变量。还请注意即使赋予 ref 或 out 形参的实参是固定变量,它们仍属于可移动变量。最后请注意,由取消指针引用而产生的变量总是属于固定变量。1.4 指针转换在不安全上下文中,可供使用的隐式转换的集合(第 6.1 节)也扩展为包括以下隐式指针转换: 从任何 pointer-type 到 void* 类型。 从 null 类型到任何 pointer-type。另外,在不安全上下文中,可供使用的显式转换的集合(第 6.2 节)也扩展为包括以下显式指针转换:

21、 从任何 pointer-type 到任何其他 pointer-type。 从 sbyte、byte、short、ushort、int、uint、long 或 ulong 到任何 pointer-type。 从任何 pointer-type 到 sbyte、byte、short、ushort、int、uint、long 或 ulong。最后,在不安全上下文中,标准隐式转换的集合(第 6.3.1 节)包括以下指针转换: 从任何 pointer-type 到 void* 类型。两个指针类型之间的转换永远不会更改实际的指针值。换句话说,从一个指针类型到另一个指针类型的转换不会影响由指针给出的基础地址

22、。当一个指针类型被转换为另一个指针类型时,如果没有将得到的指针正确地对指向的类型对齐,则当结果被取消引用时,该行为将是未定义的。一般情况下,“正确对齐”的概念是可传递的:如果指向类型 A 的指针正确地与指向类型 B 的指针对齐,而此指向类型 B 的指针又正确地与指向类型 C 的指针对齐,则指向类型 A 的指针将正确地与指向类型 C 的指针对齐。请考虑下列情况,其中具有一个类型的变量被通过指向一个不同类型的指针访问:char c = A;char* pc = &c;void* pv = pc;int* pi = (int*)pv;int i = *pi; / undefined*pi = 123

23、456; / undefined当一个指针类型被转换为指向字节的指针时,转换后的指针将指向原来所指变量的地址中的最低寻址字节。连续增加该变换后的指针(最大可达到该变量所占内存空间的大小),将产生指向该变量的其他字节的指针。例如,下列方法将 double 型变量中的八个字节的每一个显示为一个十六进制值:using System;class Test unsafe static void Main() double d = 123.456e23; unsafe byte* pb = (byte*)&d; for (int i = 0; i ”标记,最后是一个 identifier。pointer-

24、member-access:primary-expression - identifier在 P-I 形式的指针成员访问中,P 必须是除 void* 以外的某个指针类型的表达式,而 I 必须表示 P 所指向的类型的可访问成员。P-I 形式的指针成员访问的计算方式与 (*P).I 完全相同。有关指针间接寻址运算符 (*) 的说明,请参见第 18.5.1 节。有关成员访问运算符 (.) 的说明,请参见第 7.5.4 节。在下面的示例中using System;struct Point public int x; public int y; public override string ToStri

25、ng() return ( + x + , + y + ); class Test static void Main() Point point; unsafe Point* p = &point; p-x = 10; p-y = 20; Console.WriteLine(p-ToString(); - 运算符用于通过指针访问结构中的字段和调用结构中的方法。由于 P-I 操作完全等效于 (*P).I,因此 Main 方法可以等效地编写为:class Test static void Main() Point point; unsafe Point* p = &point; (*p).x = 10; (*p).y = 20; Console.WriteLine(*p).ToString(); 1.5.3 指针元素访问pointer-element-access 包括一个 primary-no-array-creation-expression,后跟一个用“”和“”括起来的表达式。pointer-element-access:primary-no-array-creation-expressi

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1