首页 > 编程语言 >.NET(C#) 中使用指针

.NET(C#) 中使用指针

时间:2024-01-24 21:44:45浏览次数:25  
标签:C# System int 内存 using NET 指针

 

在 .NET 中,C# 默认情况下是一种安全的、不支持直接使用指针的语言。然而,C# 支持在特定情况下使用不安全代码(即可以使用指针的代码)。使用不安全代码意味着放弃了 C# 和 .NET 运行时提供的一些安全保障,因此必须谨慎使用。不安全代码可能会增加程序出错的风险,特别是与内存管理相关的错误,如堆损坏、内存泄漏等。指针操作通常只在性能至关重要的情况下使用,或者在需要与系统级别或非托管代码互操作时使用。

  参考文档:.Net(C#)中使用指针-CJavaPy

1、 开启不安全代码支持

要在 C# 项目中使用指针,首先需要在项目设置中启用不安全代码。这通常在项目属性中设置。

1)Visual Studio 设置

在项目上右键属性,在点击 "生成" ,勾选允许不安代码,有些vs中那个选项可能是英文的,需要注意一下,如下图,

​编辑

2)修改.csproj 文件

在 .csproj 文件中添加以下代码:

<PropertyGroup>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

2、使用 unsafe 关键字

在 C# 中,任何包含指针操作的代码块都需要被 unsafe 关键字标记。这可以是一个方法、一个代码块或整个类。

1)使用unsafe代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
             unsafe
             {
                 int[] numbers = { 10, 20, 30, 40, 50 };
                 // 固定数组,防止垃圾回收器移动它,获取数组的指针
                 fixed (int* p = numbers)
                 {
                     // 输出数组中的每个元素
                     for (int i = 0; i < numbers.Length; i++)
                     {
                         Console.WriteLine("Element {0}: {1}", i, *(p + i));   
                      }
                  }
              }
              Console.ReadKey();
        }
    }
}

 2)使用unsafe关键字

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication
{
    class Program
    {
            // 定义一个标记了 unsafe 的方法
        public unsafe static void UnsafeMethod()
        {
            int value = 10;
            int* pValue = &value; // 获取变量的地址
    
            Console.WriteLine("Value before change: " + value);
            
            // 使用指针修改变量的值
            *pValue = 20;
            Console.WriteLine("Value after change: " + value);
        }
        static void Main(string[] args)
        {
              // 调用 unsafe 方法
              UnsafeMethod();
              Console.ReadKey();
        }
    }
}

3、指针操作

声明指针使用 操作符声明指针变量,例如 int* p;  ,获取变量地址:使用 & 操作符获取变量的地址,例如 p = &myVar;。访问指针指向的值使用 * 操作符访问或修改指针指向的值,例如 *p = 5;。 

1)基础操作

使用不安全代码时,程序员负责确保代码的正确性和内存安全性。错误的指针操作可能会导致程序崩溃或数据损坏。fixed 语句限制了垃圾回收器对固定对象的处理,因此应谨慎使用,以免影响性能。

using System;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {    
             //用指针操作栈上的值类型
             int maxValue = 10;
             int* p = &maxValue;
             *p = 20;
             Console.WriteLine(p->ToString());eadKey();
        }
    }
    //要用指针操作托管堆上的值类型,需要用到 fixed关键字
    public unsafe class Coder
     {
        public int Age;
        public void SetAge(int age)
        {
            fixed (int* p = &Age)
            {
                *p = age;
            }
        }
    }
}

2)分配释放内存

使用指针操作内存时,可以通过 Marshal 类来分配和释放非托管内存。这是因为在 .NET 中,内存管理通常是自动的,但在使用指针进行非托管内存操作时,需要手动处理内存的分配和释放。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
             // 分配非托管内存
              int size = sizeof(int);
             IntPtr pMemory =  System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
     
             try
             {
                 // 将 IntPtr 转换为指针
                 int* pInt = (int*)pMemory;
     
                 // 在分配的内存中存储数据
                 *pInt = 123;
     
                 // 读取内存中的数据
                 Console.WriteLine("Data: " + *pInt);
             }
             finally
             {
                 // 释放非托管内存
                  System.Runtime.InteropServices.Marshal.FreeHGlobal(pMemory);
             }
              Console.ReadKey();
        }
    }

}
             System.Runtime.InteropServices.Marshal.FreeHGlobal(handle);

  3)用IDispose接口管理内存

实现 IDisposable 接口允许你定义一个 Dispose 方法,用于释放类所占用的非托管资源。这是一种确定性的清理方式,可以控制何时释放资源。

代码:.Net(C#)中使用指针-CJavaPy

  使用方法:

 using (UnmanagedMemory memory = new UnmanagedMemory(10))
            {
                int* p = (int*)memory.Handle;
                *p = 20;
                Console.WriteLine(p->ToString());
            }

4)使用stackalloc关键字

stackalloc 关键字用于在栈上分配内存块,通常用于分配小型数组。与在堆上分配内存(如使用 new 关键字)不同,stackalloc 分配的内存不需要垃圾回收,因此可以提高性能。但是,它只应该用于较小的内存分配,因为栈空间相对有限。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
             /// 分配一个大小为 10 的整数数组
            int* array = stackalloc int[10];
    
            // 初始化数组
            for (int i = 0; i < 10; i++)
            {
                array[i] = i;
            }
    
            // 打印数组内容
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(array[i]);
            }
       }
    }

}

4、C# 指针操作的几个缺点

在 C# 中使用指针操作(通常在 unsafe 代码块中进行)确实提供了更直接的内存访问和潜在的性能优势,但它也带来了一些明显的缺点和风险。

1)只能用来操作值类型
指针在 C# 中主要用于操作值类型(如 int, float, char 等)。.NET中,引用类型的内存管理全部是由GC管理的,无法取得其地址,因此,无法用指针来操作引用类型。
2)泛型不支持指针类型
泛型的一个主要优点是提供类型安全。但是,由于指针操作本质上是不安全的,因此 C# 不允许在泛型类或方法中使用指针类型。这意味着你不能创建一个泛型类或方法来处理通用的指针操作,限制了泛型的灵活
3)函数指针
C# 中有delegate,delegate 支持支持指针类型,lambda 表达式也支持指针。委托是 C# 中一个非常强大的功能,它允许将方法作为参数传递给其他方法,类似于 C 和 C++ 中的函数指针。委托可以引用具有特定参数列表和返回类型的任何方法。C# 提供 delegate 类型来定义安全函数指针对象。

 参考文档:.Net(C#)中使用指针-CJavaPy

标签:C#,System,int,内存,using,NET,指针
From: https://www.cnblogs.com/tinyblog/p/17985927

相关文章

  • k8s之持久存储卷PV和PVC
    一、简介在前边文章中可以看到,Kubernetes中依赖后端存储包括:NFS、Ceph、块存储等存储设备实现数据的远程存储以及数据持久化。使用这些网络存储资源需要工程师对存储有一定的了解,并需要在工作节点上对接这些存储,对于非专业工程师造成了一些困扰。Kubernetes为了解决这些问题,定......
  • KY124 二叉搜索树C++
    先把BST建立起,然后递归遍历判断树就好了。#include<iostream>#include<string>usingnamespacestd;structnode{chardata;structnode*left;structnode*right;};typedefstructnodetree;tree*build(strings){inti=0;tree*root=NULL......
  • CSCMS V4 搬家教程
     CSCMS V4搬家教程 1、后台---系统--系统维护_备份还原,备份好数据库(主要是确保万无一失)2、整站打包传到新主机。3、修改cscms/config/sys/Cs_DB.php文件的数据库名称数据库用户名数据密码。4、如果不同域名需修改cscms/config/sys/Cs_Config.php文件里面的站点......
  • Logback排除指定包/类/方法日志输出
    排除指定包/类/方法日志输出如下为logback.xml的配置<springProfilename="pro"><appendername="BUSINESS_API"class="ch.qos.logback.core.rolling.RollingFileAppender"><file>/data/log/${projectName}/api_${p......
  • Mysql报:ERROR 145 (HY000) at line 1: Table './mysq1/proc' is marked as crashed an
    版权声明:原创作品,谢绝转载!否则将追究法律责任。—————作者:kirin先看报错mysq1:[Marning]Usingapasswordonthecommandlineinterfacecanbeinsecure.ERROR145(HY000)atline1:Table'./mysq1/proc'ismarkedascrashedandshouldberepaired1、截图如......
  • Codeforces Round 170 (Div. 1)A. Learning Languages并查集
    如果两个人会的语言中有共同语言那么他们之间就可以交流,并且如果a和b可以交流,b和c可以交流,那么a和c也可以交流,具有传递性,就容易联想到并查集,我们将人和语言看成元素,一个人会几种语言的话,就将这些语言和这个人所在的集合合并,最后求一下人一共在几个连通块中,连通块的个数-1就是答案,......
  • C# 方法详解:定义、调用、参数、默认值、返回值、命名参数、方法重载全解析
    C#Methods方法是一段代码,只有在调用时才会运行。您可以将数据(称为参数)传递给方法。方法用于执行某些操作,也被称为函数。为什么使用方法?为了重用代码:定义一次代码,然后多次使用。创建一个方法方法的定义以方法的名称开头,后跟括号()。C#提供了一些预定义的方法,您已经熟悉,例如M......
  • Voxel R-CNN 代码解析
    1.网络主体架构以voxel_rcnn_car.yaml为例。主要包括:VFE(体素编码网络),这里采用下MeanVFE;BACKBONE_3D,这里采用VoxelBackBone8x;MAP_TO_BEV,这里采用HeightCompression,NUM_BEV_FEATURES为256;BACKBONE_2D,这里采用BaseBEVBackbone;DENSE_HEAD,这里采用AnchorHeadSingle;ROI_H......
  • webstorm报错:ESLint: TypeError: this.libOptions.parse is not a function
     解决办法:把 {**/*,*}.{js,ts,jsx,tsx,html,vue}换成{**/*,*}.(js,ts,jsx,tsx,html,vue) 原文:https://stackoverflow.com/questions/73509984/eslint-typeerror-this-liboptions-parse-is-not-a-functionMyfriends,ifyouareusing Webstorm orany Jetbrains p......
  • cprimerplus代码相关汇总
    第一章初识C语言重点内容起源:1972,贝尔实验室。继承B语言。特点:功能强大,应用范围广泛。设计步骤:1.定义程序目标2.设计程序3.编写代码4.编译5.运行程序6.测试和调试程序7.维护和修改程序本章小结C是强大而简洁的编程语言。它之所以流行,在于自身提供大量的实用编程工具,能很好......