序号系列文章20【C#进阶】C# 泛型21【C#进阶】C# 匿名方法22【C#进阶】C# 多线程
文章目录
前言
📔 大家好啊,我是哈桑c,本文为大家介绍 C# 中的非托管代码。
1、什么是不安全代码?
不安全代码又称非托管代码,在 C# 中不安全的代码可以使用指针、分配和释放内存块,以及使用函数指针调用方法。注意不安全的代码并不一定是危险的,只是其安全性不可验证的代码。
在 C# 程序中可以使用 unsafe 上下文来编写不安全的代码,一般我们编写的大部分 C# 代码都是“可验证的安全代码”。可验证的安全代码是指 .NET 框架可验证代码是否安全。
代码示例: (使用 unsafe 上下文一段输出指针变量的代码。)
unsafe// unsafe 关键字声明不安全代码{int num1 =20;int* p =&num1;
Console.WriteLine($"数据是: {num1} ");
Console.WriteLine($"数据地址是: {(int)p}");
Console.ReadLine();}/*
输出:
数据是: 20
数据地址是: 5763444
*/
从上例中看出,在 unsafe 关键字声明的语句块内的代码可以使用指针输出 num 对象的数据地址。
usingSystem;// unsafe 修饰符可以修饰整个类publicunsafeclassUnsafeCodeSample{// 也可以修饰整个方法,通常只需要声明其中一种,这里为了方便演示。staticunsafevoidMain(string[] args){int num1 =20;int* p =&num1;
Console.WriteLine($"数据是: {num1} ");
Console.WriteLine($"数据地址是: {(int)p}");
Console.ReadLine();}}/*
输出:
数据是: 20
数据地址是: 41414304
*/
在声明方法签名时,也可以使用 unsafe 修饰符声明整个方法或类为不安全代码。本文主要使用前者演示不安全代码的使用。
2、如何编译不安全代码?
在 Visual Studio 中右击报错的项目选择属性,找到生成里面的 允许使用“unsafe"关键字编译的代码 ,勾选即可。
点击查看详细步骤演示。
3、指针类型
在 C# 中,数据类型除了可以是值类型或引用类型之外,还可以是指针类型。可以使用 * 符号来声明类型变量的地址,比如 Type* 类型的指针变量的值为 Type 类型的变量的地址。
注意指针类型只能在不同的指针类型之间以及指针类型和整型之间进行转换,而且指针类型不能指向引用对象。
指针类型的声明语法:
当在同一声明中声明多个指针时,星号 (*) 仅与基础类型一起写入, 而不是用作每个指针名称的前缀。
int* p1, p2, p3;int*p1,*p2,*p3;// 在C#中无效
以一个表格展示指针类型声明的示例:
示例描述int* pp 是指向整数的指针。int** pp 是指向整数的指针的指针。int*[] pp 是指向整数的指针的一维数组。char* pp 是指向字符的指针。void* pp 是指向未知类型的指针。
代码示例:
unsafe{int num1 =11;int* p1 =&num1;// 指向整数的指针。
Console.WriteLine($"指向整数num1的地址为{(int)p1}");int** p2 =&p1;// 指向整数的指针的指针。
Console.WriteLine($"指向整数num1的指针的地址为{(int)p2}");int*[] p3 =newint*[]{p1};// 指向整数num1的指针的一维数组
Console.WriteLine($"指向整数num1的指针的一维数组为{p3}");char c1 ='c';char* p4 =&c1;// 指向字符的指针
Console.WriteLine($"指向字符的地址为{(int)p4}");void* p5 =&p4;// 指向未知类型的指针
Console.WriteLine($"指向未知类型的地址为{(int)p5}");/*
输出:
指向整数num1的地址为46002224
指向整数num1的指针的地址为46002220
指向整数num1的指针的一维数组为System.Int32*[]
指向字符的地址为46002208
指向未知类型的地址为46002204
*/}
4、指针执行的运算符和语句
以一个表格展示可在不安全的上下文中对指针执行的运算符和语句:
运算符/语句使用*执行指针间接寻址。->通过指针访问结构的成员。[]为指针建立索引。&获取变量的地址。++ 和 –递增和递减指针。+ 和 -执行指针算法。==、!=、<、>、<= 和 >=比较指针。stackalloc在堆栈上分配内存。语句临时固定变量以便找到其地址。
5、固定大小的缓冲区
在 C# 中,fixed 关键字可以创建在数据结构中具有固定大小的数组的缓冲区。通俗的讲就是 fixed 关键字能够将指定的数组给固定住,并返回固定后的指针。这是因为堆上对象的实际地址是不固定而受 GC 控制的。当需要编写与其他语言或平台的数据源进行互操作的方法时,就可以使用固定大小的数组的缓冲区。
使用 fixed 的唯一限制就是数组的类型必须为 bool、byte、char、short、int, long、sbyte、ushort、uint、ulong、float 或 double 。
代码示例:
usingSystem;publicclassUnsafeCodeSample{// 在访问修饰符后面声明unsafe修饰符publicunsafestaticvoidMain(){int[] list1 ={10,100,200};fixed(int* ptr = list1)/* 显示指针中数组地址 */for(int i =0; i <3; i++){
Console.WriteLine($"list1的地址[{i}]={(int)(ptr + i)}");
Console.WriteLine($"list1的元素值[{i}]={*(ptr + i)}");}}}/*
输出:
list1的地址[0]=90230144
list1的元素值[0]=10
list1的地址[1]=90230148
list1的元素值[1]=100
list1的地址[2]=90230152
list1的元素值[2]=200
*/
从上例的输出结果可以看出,使用 fixed 关键字可以使用指针变量访问数组数据。
固定大小的缓冲区与常规数组的区别体现在以下几个方面:
- 需要在 unsafe 上下文中使用。
- 只能是结构的实例字段。
- 它们始终是矢量或一维数组。
- 声明固定大小的数组时应包括长度,如 fixed char id[8]。 不能使用 fixed char id[]。
6、函数指针
函数指针指的是函数编码后的指令在内存中的首个地址。在 C# 中的 delegate 类型用来定义安全函数指针对象, 而 delegate* 语法可以用来定义函数指针。
代码示例:
usingSystem;publicunsafeclassFunctionPointer{publicstaticTUnsafeCombine<T>(delegate*<T, T, T> combinator,T left,T right)=>combinator(left, right);staticintlocalMultiply(int x,int y)=> x * y;staticvoidMain(string[] args){int product =UnsafeCombine(&localMultiply,6,4);
Console.WriteLine(product);}}/*
输出:
24
*/
在上面的示例中,我们将 localMultiply 方法的地址当作参数传递给了函数指针 combinator ,并成功执行输出了结果 24。
7、不安全代码的总结
不安全代码的属性可总结为以下几点:
- 可将方法、类型和代码块定义为不安全。
- 有时候通过移除数组检测,不安全代码可提高程序的性能。
- 不安全代码常用于调用需要指针的本机函数时。
- 使用不安全代码需要承担对应的安全风险和稳定性风险。
- 运行不安全代码需要使用允许不安全代码的编译器。
点击了解更多不安全代码的使用。
结语
📚 以上就是 C# 不安全代码的介绍啦,希望对大家有所帮助。感谢大家的支持。
版权归原作者 哈桑c 所有, 如有侵权,请联系我们删除。