【C#进阶】C# 不安全代码

这篇具有很好参考价值的文章主要介绍了【C#进阶】C# 不安全代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

序号 系列文章
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 对象的数据地址。

using System;

// unsafe 修饰符可以修饰整个类
public unsafe class UnsafeCodeSample
{
    // 也可以修饰整个方法,通常只需要声明其中一种,这里为了方便演示。
    static unsafe void Main(string[] args)
    {
        int num1 = 20;
        int* p = &num1;
        Console.WriteLine($"数据是: {num1} ");
        Console.WriteLine($"数据地址是: {(int)p}");
        Console.ReadLine();
    }
}

/*
输出:
    数据是: 20
    数据地址是: 41414304
 */

在声明方法签名时,也可以使用 unsafe 修饰符声明整个方法或类为不安全代码。本文主要使用前者演示不安全代码的使用。

2、如何编译不安全代码?

在 Visual Studio 中右击报错的项目选择属性,找到生成里面的 允许使用“unsafe"关键字编译的代码 ,勾选即可。

【C#进阶】C# 不安全代码
点击查看详细步骤演示。

3、指针类型

在 C# 中,数据类型除了可以是值类型或引用类型之外,还可以是指针类型。可以使用 * 符号来声明类型变量的地址,比如 Type* 类型的指针变量的值为 Type 类型的变量的地址。

注意指针类型只能在不同的指针类型之间以及指针类型和整型之间进行转换,而且指针类型不能指向引用对象。

指针类型的声明语法:

当在同一声明中声明多个指针时,星号 (*) 仅与基础类型一起写入, 而不是用作每个指针名称的前缀。

int* p1, p2, p3;
int *p1, *p2, *p3;   // 在C#中无效

【C#进阶】C# 不安全代码

以一个表格展示指针类型声明的示例:

示例 描述
int* p p 是指向整数的指针。
int** p p 是指向整数的指针的指针。
int*[] p p 是指向整数的指针的一维数组。
char* p p 是指向字符的指针。
void* p p 是指向未知类型的指针。

代码示例:

unsafe
{
    int num1 = 11;
    int* p1 = &num1;

    // 指向整数的指针。 
    Console.WriteLine($"指向整数num1的地址为{(int)p1}");

    int** p2 = &p1;
    // 指向整数的指针的指针。
    Console.WriteLine($"指向整数num1的指针的地址为{(int)p2}");

    int*[] p3 = new int*[] {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 关键字能够将指定的数组给固定住,并返回固定后的指针。这是因为堆上对象的实际地址是不固定而受 GC1 控制的。当需要编写与其他语言或平台的数据源进行互操作的方法时,就可以使用固定大小的数组的缓冲区。

使用 fixed 的唯一限制就是数组的类型必须为 bool、byte、char、short、int, long、sbyte、ushort、uint、ulong、float 或 double 。

代码示例:

using System;

public class UnsafeCodeSample
{
    // 在访问修饰符后面声明unsafe修饰符
    public unsafe static void Main()
    {
        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* 语法可以用来定义函数指针。

代码示例:

using System;

public unsafe class FunctionPointer
{
    public static T UnsafeCombine<T>(delegate*<T, T, T> combinator, T left, T right) =>
        combinator(left, right);

    static int localMultiply(int x, int y) => x * y;

    static void Main(string[] args)
    {
        int product = UnsafeCombine(&localMultiply, 6, 4);

        Console.WriteLine(product);
    }
}

/*
输出:
    24
*/

在上面的示例中,我们将 localMultiply 方法的地址当作参数传递给了函数指针 combinator ,并成功执行输出了结果 24。

7、不安全代码的总结

不安全代码的属性可总结为以下几点:

  • 可将方法、类型和代码块定义为不安全。
  • 有时候通过移除数组检测,不安全代码可提高程序的性能。
  • 不安全代码常用于调用需要指针的本机函数时。
  • 使用不安全代码需要承担对应的安全风险和稳定性风险。
  • 运行不安全代码需要使用允许不安全代码的编译器。

点击了解更多不安全代码的使用。


结语

📚 以上就是 C# 不安全代码的介绍啦,希望对大家有所帮助。感谢大家的支持。


  1. GC: 全称为 garbage collection,中文名称为垃圾回收机制,是 .net 框架中对内存管理的一种技术。 ↩︎文章来源地址https://www.toymoban.com/news/detail-428676.html

到了这里,关于【C#进阶】C# 不安全代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 用这些C#代码混淆器保护你的代码安全

    最近有群友问,怎么保护发布的C#代码不被别人反编译,这就需要C#代码混淆组件。C#是一种强大的编程语言,可以用于开发各种类型的应用程序,包括桌面应用程序、Web应用程序和移动应用程序等。但是,由于C#程序易于反编译,使得代码的安全性和保护性受到了威胁。为了

    2024年02月05日
    浏览(46)
  • c#优雅高效的读取字节数组——不安全代码(1)

    在开发上位机的经历中,会有很多需要和下位机交互通信的场景,大多数都会定义一个和硬件的通信协议,最终在上位机代码中的形式其实就是符合通信协议的字节数组。 目录 场景 如何解析字节数组到类或结构体中 建立与通信协议一致的结构体 使用不安全代码将字节数组

    2024年02月06日
    浏览(37)
  • C# 泛型类型详解:编写更安全、可重用的代码

    在C#中,泛型类型是一种强大的特性,它允许我们编写更加灵活和可重用的代码。本文将详细介绍泛型类型的概念、优势以及使用方法,并提供一些示例来帮助新手更好地理解 泛型类型是一种在编译时能够指定类型参数的类型。它允许我们在定义类、接口、方法等时,不指定

    2024年02月20日
    浏览(48)
  • unity的C#学习——不安全代码(声明不安全代码块:实现C/C++指针的创建与相关操作)

    不安全代码(Unsafe code)是指 使用了指针变量的代码块 ,或者 执行需要访问非托管代码(unmanaged code)的操作 。 指针 是一个变量,其值为另一个变量的地址,即内存位置的直接地址。在C#中,不安全代码必须使用 unsafe 修饰符标记,并需要在编译时启用 AllowUnsafeCode 选项。

    2024年02月09日
    浏览(41)
  • 【C#进阶】C# 特性

    序号 系列文章 10 【C#基础】C# 正则表达式 11 【C#基础】C# 预处理器指令 12 【C#基础】C# 文件与IO 👍 大家好,我是writer桑,本章为大家介绍 C# 中的 特性 。 特性 (Attribute) 是用于在运行时传递程序中各种元素(比如类、方法、结构、组件等)的行为信息的 声明性标签 。可以使

    2024年02月02日
    浏览(30)
  • 【C#进阶】C# 属性

    序号 系列文章 12 【C#基础】C# 文件与IO 13 【C#进阶】C# 特性 14 【C#进阶】C# 反射 🌲 大家好,我是writer桑,本章为大家介绍 C# 中的 属性 。 属性 是 C# 中的重要成员。 属性和字段 1 类似,但属性提供了一种被称为访问器的灵活机制来 读取、写入或计算私有字段的值 。访问器

    2024年02月11日
    浏览(30)
  • 【C#进阶】C# 事件

    序号 系列文章 15 【C#进阶】C# 属性 16 【C#进阶】C# 索引器 17 【C#进阶】C# 委托 🌍 hello大家好啊,我是哈桑。本文为大家介绍 C# 中的事件。 事件 本质上来讲是一种特殊的多播委托 1 ,只能从声明它的类中进行调用。事件通常用于表示用户操作,例如单击按钮或图形用户界面

    2024年02月07日
    浏览(39)
  • 【C#进阶】C# 反射

    序号 系列文章 11 【C#基础】C# 预处理器指令 12 【C#基础】C# 文件与IO 13 【C#进阶】C# 特性 ✋ 大家好,我是writer桑,本章为大家介绍 C# 中的 反射 。 反射 指的是程序可以 访问,检测和修改 它本身状态或行为的一种行为。 其中访问的目标包括程序集 1 、模块和类型对象等。可

    2024年03月15日
    浏览(49)
  • 【C#进阶】C# 匿名方法

    序号 系列文章 18 【C#进阶】C# 事件 19 【C#进阶】C# 集合类 20 【C#进阶】C# 泛型 📺 hello大家好啊,我是哈桑c,本文为大家介绍 C# 中的匿名方法。 匿名方法 顾名思义就是这类方法的特点是不需要特别去定义函数的名字的。一般我们需要一个函数,但又不想花时间去命名它的时

    2024年02月04日
    浏览(34)
  • 【C#进阶】C# 集合类

    序号 系列文章 16 【C#进阶】C# 索引器 17 【C#进阶】C# 委托 18 【C#进阶】C# 事件 🌄 hello大家好啊,我是哈桑,本章为大家介绍 C# 中的集合类。 集合类 是专门用于数据存储和检索的类。在这些类中实现了对列表、队列、哈希表等数据结构的封装以及对操作数据的支持。当在项

    2024年02月09日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包