欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在C#中使用指針的示例代碼

 更新時(shí)間:2024年10月29日 10:13:35   作者:△曉風(fēng)殘?jiān)漏e  
C#向開發(fā)人員隱藏了大部分基本內(nèi)存管理操作,因?yàn)樗褂昧死厥掌骱鸵?但是,有時(shí)候我們也需要直接訪問內(nèi)存,例如:進(jìn)行平臺(tái)調(diào)用,性能優(yōu)化等等,本文給大家介紹了在C#中使用指針的示例代碼,需要的朋友可以參考下

.Net平臺(tái)定義了兩種主要數(shù)據(jù)類型:值類型和引用類型,其實(shí)還有第三種數(shù)據(jù)類型:指針類型。使用指針,可以繞開CLR的內(nèi)存管理機(jī)制。(說明:在C#中使用指針,需要有相關(guān)C/C++指針操作基礎(chǔ))

1、C#中指針相關(guān)的操作符和關(guān)鍵字

操作符/關(guān)鍵字作用
*該操作符用于創(chuàng)建一個(gè)指針變量,和在C/C++中一樣。也可用于指針間接尋址(解除引用)
&該操作符用于獲取內(nèi)存中變量的地址
->該操作符用于訪問一個(gè)由指針表示的類型的字段,和在C++中一樣
[]在不安全的上下文中,[]操作符允許我們索引由指針變量指向的位置
++,--在不安全的上下文中,遞增和遞減操作符可用于指針類型
+,-在不安全的上下文中,加減操作符可用于指針類型
==, !=, <, >, <=, >=在不安全的上下文中,比較和相等操作符可用于指針類型
stackalloc在不安全的上下文中,stackalloc關(guān)鍵字可用于直接在棧上分配C#數(shù)組,類似CRT中的_alloca函數(shù) 
 fixed在不安全的上下文中,fixed關(guān)鍵字可用于臨時(shí)固定一個(gè)變量以使它的地址可被找到

 2、在C#中使用指針,需要啟用“允許不安全代碼”設(shè)置

選擇項(xiàng)目屬性->生成,鉤上“允許不安全代碼”

3、unsafe關(guān)鍵字

只有在unsafe所包含的代碼區(qū)塊中,才能使用指針。類似lock關(guān)鍵字的語法結(jié)構(gòu)

除了聲明代碼塊為不安全代碼外,也可以直接構(gòu)建“不安全的”結(jié)構(gòu)、類型成員和函數(shù)。

   unsafe struct Point
         {
             public int x;
             public int y;
             public Point* next;
             public Point* previous;
         }
    unsafe static void CalcPoint(Point* point)
         {
             //
         }

 也可以在導(dǎo)入非托管 DLL 的函數(shù)聲明中使用unsafe

  [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
  private static extern unsafe int memcpy(void* dest, void* src, int count);

注意:

指針不能指向引用或包含引用的結(jié)構(gòu),因?yàn)闊o法對(duì)對(duì)象引用進(jìn)行垃圾回收,即使有指針指向它也是如此。 垃圾回收器并不跟蹤是否有任何類型的指針指向?qū)ο蟆?/strong>

下面的示例代碼可以說明:

    /// <summary>
    /// 聲明一個(gè)Point結(jié)構(gòu)體
    /// </summary>
    struct Point
    {
        public int x;
        public int y;      
    }

      static void Main(string[] args)
        {
            unsafe
            {
                //編譯正常
                Point p = new Point();
                Point* pp = &p;
            }
        }
  //換成類
  class Point
     {
         public int x;
         public int y;      
     }

 4、*和&操作符

在不安全的上下文中,可以使用 操作符構(gòu)建數(shù)據(jù)類型相對(duì)應(yīng)的指針類型(指針類型、值類型和引用類型,示例代碼中的type),使用 操作符獲取被指向的內(nèi)存地址。

 type* identifier;
 void* identifier; //允許但不推薦

 下面是使用*操作符進(jìn)行指針類型聲明

int* pp 是指向整數(shù)的指針。
int** pp 是指向整數(shù)的指針的指針。
int*[] pp 是指向整數(shù)的指針的一維數(shù)組。
char* pp 是指向字符的指針。
void* pp 是指向未知類型的指針。

注意:

1、無法對(duì) void* 類型的指針應(yīng)用間接尋址運(yùn)算符。 但是,你可以使用強(qiáng)制轉(zhuǎn)換將 void 指針轉(zhuǎn)換為任何其他指針類型,反過來也是可以的。

2、指針類型不從object類繼承,并且指針類型與 object 之間不存在轉(zhuǎn)換。 此外,裝箱和取消裝箱不支持指針。 

下面的代碼演示了如何聲明指針類型:

 static void Main(string[] args)
         {
             int []a = { 1, 2, 3, 4, 4 };
 
             unsafe
             {
                 //臨時(shí)固定一個(gè)變量以使它的地址可被找到
                 fixed (int* p = &a[0])
                 {
                     int* p2 = p;
                     Console.WriteLine(*p2);
                     p2++;
                     Console.WriteLine(*p2);
                     p2++;
                     Console.WriteLine(*p2);
                 }
             }
 
         }

 輸出結(jié)果如下:

1

2

3

下面的代碼演示了如何使用指針類型進(jìn)行數(shù)據(jù)交換:

 static void Main(string[] args)
         {
             int a = 1;
             int b = 2;
 
             unsafe
             {
                 UnsafeSwap(&a, &b);
             }
 
             Console.WriteLine(a);
             Console.WriteLine(b);
         }
 
         /// <summary>
         /// 使用指針
         /// </summary>
         /// <param name="a"></param>
         /// <param name="b"></param>
         static unsafe void UnsafeSwap(int* a,int *b)
         {
             int temp = *a;
             *a = *b;
             *b = temp;
         }
 
         /// <summary>
         /// 不使用指針的安全版本
         /// </summary>
         /// <param name="a"></param>
         /// <param name="b"></param>
         static void SafeSwap(ref int a,ref int b)
         {
             int temp = a;
             a = b;
             b = temp;
         }

輸出結(jié)果如下:

2

1

5、通過指針訪問字段

定義如下結(jié)構(gòu)體

  struct Point
         {
             public int x;
             public int y;
 
             public override string ToString()
             {
                 return $"x:{x},y:{y}";
             }
         }

如果聲明一個(gè)Point類型的指針,就需要使用指針字段訪問操作符(->)來訪問公共成員(和C++一樣),也可以使用指針間接尋址操作符(*)來解除指針的引用,使其也可以使用 (.)操作符訪問字段(和C++一樣)。

 static unsafe void Main(string[] args)
         {
             //通過指針訪問成員
             Point point = new Point();
             Point* p = &point;
             p->x = 10;
             p->y = 5;
             Console.WriteLine(p->ToString());
 
             //通過指針間接尋址訪問成員
             Point point2; //不使用 new 運(yùn)算符的情況下對(duì)其進(jìn)行實(shí)例化,需要在首次使用實(shí)例之前必須初始化所有實(shí)例字段。
             Point* p2 = &point2;
             (*p2).x = 128;
             (*p2).y = 256;
             Console.WriteLine((*p2).ToString());
         }

運(yùn)行結(jié)果如下:

x:10,y:5
x:128,y:256

6、stackalloc關(guān)鍵字

在不安全上下文中,可能需要聲明一個(gè)直接從調(diào)用棧分配內(nèi)存的本地變量(不受.Net垃圾回收器控制)。C#提供了與CRT函數(shù)_alloca等效的stackalloc關(guān)鍵字來滿足這個(gè)需求。

 static unsafe void Main(string[] args)
         {
             char* p = stackalloc char[3];
 
             for (int i = 0; i < 3; i++)
             {
                 p[i] = (char)(i+65); //A-C
             }
 
             Console.WriteLine(*p);
             Console.WriteLine(p[0]);
 
             Console.WriteLine(*(++p));
             Console.WriteLine(p[0]);
 
             Console.WriteLine(*(++p));
             Console.WriteLine(p[0]);
         }

輸出結(jié)果如下:

A
A
B
B
C
C

7、fixed關(guān)鍵字

在上面的示例中,我們可以看到,通過stackalloc關(guān)鍵字,在不安全上下文中分配一大塊內(nèi)存非常方便。但這塊內(nèi)存是在棧上的,當(dāng)分配方法返回的時(shí)候,被分配的內(nèi)存立即被清理。

假設(shè)有如下情況:

聲明一個(gè)引用類型PointRef和一個(gè)值類型Point

     class PointRef
         {
             public int x;
             public int y;
 
             public override string ToString()
             {
                 return $"x:{x},y:{y}";
             }
         }
 
         struct Point
         {
             public int x;
             public int y;
 
             public override string ToString()
             {
                 return $"x:{x},y:{y}";
             }
         }

調(diào)用者聲明了一個(gè)PointRef類型的變量,內(nèi)存將被分配在垃圾回收器堆上。如果一個(gè)不安全的上下文要與這個(gè)對(duì)象(或這個(gè)堆上的任何對(duì)象)交互,就可能會(huì)出現(xiàn)問題,因?yàn)槔厥湛呻S時(shí)發(fā)生。設(shè)想一下,恰好在清理堆的時(shí)候訪問Point成員,這就很

為了將不安全上下文中的引用類型變量固定,C#提供了fixed關(guān)鍵字,fixed語句設(shè)置指向托管類型的指針并在代碼執(zhí)行過程中固定該變量。換句說話:fixed關(guān)鍵字可以鎖定內(nèi)存中的引用變量。這樣在語句的執(zhí)行過程中,該變量地址保持不變。

事實(shí)上,也只有使用fixed關(guān)鍵字,C#編譯器才允許指針指向托管變量。

 static unsafe void Main(string[] args)
         {
             PointRef pointRef = new PointRef();
             Point point = new Point();
 
             int a = &pointRef.x;  //編譯不通過
 
             int *b = &point.x;    //編譯通過
 
             fixed(int *c = &pointRef.x)
             {
                 //編譯通過
             }
         }

說明:

在fixed中初始化多個(gè)變量也是可以的

            //同時(shí)聲明多個(gè)指針變量的語法跟C++中的不一樣,需要注意
             fixed(int *e = &(pointRef.x) , f = &(pointRef.y) )
             {
 
             }

8、sizeof關(guān)鍵字

在不安全上下文中,sizeof關(guān)鍵字用于獲取值類型(不是引用類型)的字節(jié)大小。sizeof可計(jì)算任何由System.ValueType派生實(shí)體的字節(jié)數(shù)。

    static  void Main(string[] args)
         {
             unsafe
             {
                 //不安全版本
                 Console.WriteLine(sizeof(int));
                 Console.WriteLine(sizeof(float));
                 Console.WriteLine(sizeof(Point));
             }
 
             //安全版本
             Console.WriteLine(Marshal.SizeOf(typeof(int)));
             Console.WriteLine(Marshal.SizeOf(typeof(float)));
             Console.WriteLine(Marshal.SizeOf(typeof(Point)));
 
         }

9、避免使用指針

事實(shí)上在C#中,指針并不是新東西。因?yàn)樵诖a中可以自由使用引用 ,而引用就是一個(gè)類型安全的指針。指針只是一個(gè)存儲(chǔ)地址的變量,這和引用其實(shí)是一個(gè)原理。引用的主要作用是使C#更易于使用,防止用戶無意中執(zhí)行某些破壞內(nèi)存中內(nèi)容的操作。

使用指針后,可以進(jìn)行低級(jí)的內(nèi)存訪問,但這是有代價(jià)的,使用指針的語法比引用類型的語法復(fù)雜得多,而且指針使用起來也比較困難,需要較高的編程技巧和強(qiáng)力。如果不仔細(xì),就容易在程序中引入細(xì)微的,難以查找的錯(cuò)誤。另外,如果使用指針,就必須授予代碼運(yùn)行庫的代碼訪問安全機(jī)制的高級(jí)別信任,否則就不能執(zhí)行它。

MSDN上有如下關(guān)于指針的說明:

在公共語言運(yùn)行時(shí) (CLR) 中,不安全代碼是指無法驗(yàn)證的代碼。 C# 中的不安全代碼不一定是危險(xiǎn)的;只是 CLR 無法驗(yàn)證該代碼的安全性。 因此,CLR 將僅執(zhí)行完全信任的程序集中的不安全代碼。 如果你使用不安全代碼,你應(yīng)該負(fù)責(zé)確保代碼不會(huì)引發(fā)安全風(fēng)險(xiǎn)或指針錯(cuò)誤。

大多數(shù)情況下,可以使用System.Intptr或ref關(guān)鍵字來替代指針完成我們想要的操作。

下面使用示例代碼說明一下:(僅供演示)

這里還是以memcpy函數(shù)為例,假設(shè)我有一個(gè)Point結(jié)構(gòu)的實(shí)例,要對(duì)這個(gè)Point進(jìn)行拷貝。

聲明Point結(jié)構(gòu)

 struct Point
     {
         public int x;
         public int y;
     }

使用System.IntPtr:

         /// <summary>
         /// 使用IntPtr
         /// </summary>
         /// <param name="pDst"></param>
         /// <param name="pSrc"></param>
         /// <param name="count"></param>
         /// <returns></returns>
         [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl)]
         private static extern unsafe int memcpyi(IntPtr pDst, IntPtr pSrc, int count);
         static void MemCpyIntPtr()
         {
             var p = new Point() { x = 200,y = 10};
             Console.WriteLine(p.x + " " + p.y);
  
             var size = Marshal.SizeOf(p);
             
             IntPtr ptrSrc = Marshal.AllocHGlobal(size);
             IntPtr ptrDest = Marshal.AllocHGlobal(size);
 
             //將結(jié)構(gòu)體Point轉(zhuǎn)換成ptrSrc
             Marshal.StructureToPtr(p, ptrSrc, false);
 
             //memcpy
             memcpyi(ptrDest, ptrSrc, size);
 
             //再轉(zhuǎn)換成結(jié)構(gòu)體
             Point p2 = new Point();
             //先輸出一次進(jìn)行對(duì)比
             Console.WriteLine(p2.x + " " + p2.y);
 
             p2 = (Point)Marshal.PtrToStructure(ptrDest, typeof(Point));
             Console.WriteLine(p2.x + " " + p2.y);
 
         }

運(yùn)行結(jié)果如下:

200 10
0 0
200 10

使用指針:

         /// <summary>
         /// 使用指針
         /// </summary>
         /// <param name="pDst"></param>
         /// <param name="pSrc"></param>
         /// <param name="count"></param>
         /// <returns></returns>
         [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl)]
         private static extern unsafe int memcpyp(void* pDst, void* pSrc, int count);
         static unsafe void MemCpyPointer()
         {
             Point p = new Point() { x = 200, y = 10 };
             Point p2 = new Point();
 
             Console.WriteLine(p.x + " " + p.y);
             Console.WriteLine(p2.x + " " + p2.y);
 
             Point* pSrc = &p;
             Point* pDest = &p2;
 
             memcpyp((void*)pDest, (void*)pSrc, sizeof(Point));
 
             p2 = *pDest;
             Console.WriteLine(p2.x + " " + p2.y);
         }

運(yùn)行結(jié)果如下:

200 10
0 0
200 10

下面介紹使用指針傳遞時(shí)的另外一種情況,這種情況我們可以使用ref來代替指針完成操作。

先用C++封裝一個(gè)庫,導(dǎo)出如下函數(shù),用來打印一個(gè)整形數(shù)組

 extern "C" __declspec(dllexport) void PrintArray(int* pa,int size);
 
 
 extern "C" __declspec(dllexport) void PrintArray(int* pa,int size)
 {
     for (size_t i = 0; i < size; i++)
     {
         std::cout << *pa << std::endl;
         pa++;
     }
 }

使用ref:

 [DllImport("demo_lib.dll",EntryPoint = "PrintArray")]
 private static extern void PrintArrayRef(ref int pa,int size);
         static void PrintArrayRef()
         {
             int[] array = new int[] { 1,2,3};
 
             //使用ref關(guān)鍵字傳的是引用,ref[0]其實(shí)就是傳的首地址
            PrintArrayRef(ref array[0], array.Length);
         }

運(yùn)行結(jié)果:

1
2
3

使用指針:

         [DllImport("demo_lib.dll", EntryPoint = "PrintArray")]
         private static extern unsafe void PrintArrayPointer(int* pa, int size);
         static unsafe void PrintArrayPointer()
         {
             int size = 3;
             int* array = stackalloc int[3];
 
             for (int i = 0; i < size; i++)
             {
                 array[i] = i+1;
             }
             PrintArrayPointer(array, size);
         }

運(yùn)行結(jié)果:

1
2
3

以上就是在C#中使用指針的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C#使用指針的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C#泛型委托的用法實(shí)例分析

    C#泛型委托的用法實(shí)例分析

    這篇文章主要介紹了C#泛型委托的用法,以實(shí)例形式較為詳細(xì)的分析了C#委托的功能與相關(guān)使用技巧,需要的朋友可以參考下
    2015-05-05
  • C#中string用法實(shí)例詳解

    C#中string用法實(shí)例詳解

    這篇文章主要介紹了C#中string用法,非常詳細(xì)的總結(jié)了比較常見的關(guān)于C#中string的幾個(gè)常用方法,需要的朋友可以參考下
    2014-08-08
  • c# 如何用lock解決緩存擊穿

    c# 如何用lock解決緩存擊穿

    這篇文章主要介紹了c# 如何用lock解決緩存擊穿,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2021-02-02
  • C# WebService發(fā)布以及IIS發(fā)布

    C# WebService發(fā)布以及IIS發(fā)布

    這篇文章主要介紹了C# WebService發(fā)布以及IIS發(fā)布的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-07-07
  • C#單位轉(zhuǎn)換器簡單案例

    C#單位轉(zhuǎn)換器簡單案例

    這篇文章主要為大家詳細(xì)介紹了C#單位轉(zhuǎn)換器簡單案例,一個(gè)簡單的winform應(yīng)用程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • C#隱藏主窗口的方法小結(jié)

    C#隱藏主窗口的方法小結(jié)

    這篇文章主要介紹了C#隱藏主窗口的方法,列舉了C#隱藏窗口的三種常用方法,涉及C#窗體操作的常用技巧,需要的朋友可以參考下
    2016-03-03
  • C# PictureBox圖片控件實(shí)現(xiàn)圖片交換

    C# PictureBox圖片控件實(shí)現(xiàn)圖片交換

    在c#中可以使用PictureBox控件來呈現(xiàn)圖像,本文主要介紹了C# PictureBox實(shí)現(xiàn)圖片交換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C#動(dòng)態(tài)代碼生成控件后其他事件不能獲取該控件值的解決方法

    C#動(dòng)態(tài)代碼生成控件后其他事件不能獲取該控件值的解決方法

    這篇文章主要給大家介紹了關(guān)于C#動(dòng)態(tài)代碼生成控件后其他事件不能獲取該控件值的解決方法,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • C#從字符串中指定位置移除子字符串的方法

    C#從字符串中指定位置移除子字符串的方法

    這篇文章主要介紹了C#從字符串中指定位置移除子字符串的方法,涉及C#中Remove方法的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • C#運(yùn)行程序時(shí)阻止關(guān)閉顯示器和系統(tǒng)待機(jī)

    C#運(yùn)行程序時(shí)阻止關(guān)閉顯示器和系統(tǒng)待機(jī)

    這篇文章介紹了C#運(yùn)行程序時(shí)阻止關(guān)閉顯示器和系統(tǒng)待機(jī)的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06

最新評(píng)論