C#指针使用学习总结
生活随笔
收集整理的這篇文章主要介紹了
C#指针使用学习总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C#中指針的使用
http://www.cnblogs.com/caibq/archive/2011/09/11/Cai_Yao.html在C#中,有時候希望通過指針來操作內存,這樣可以提高效率。我們可以用unsafe關鍵字修飾含有指針操作的程序段,如下所示:
class Program { static int Main(string[] args) {unsafe { // ... } } }
unsafe只是編譯器指令語法,只是在編譯的時候起作用,并不是說unsafe修飾的程序段是unmanaged,他們仍然是managed的。
在C#里定義指針和C++有些不同:
1. C#指針定義的星號只能和類型在一起
? ? ?int* p1, p2; ? ?//p1和p2都是int*指針
? ? ?而在C++中與之等價的定義是:
? ? ?int *p1, *p2; ?//C++
? ? ?int *p1, p2; ? //C++中,p2只是一個普通的int
2. C#指針類型不能是引用類型,不能是含有引用類型的自定義類型或復合類型,只能是預定義類型
? ? string* pMessage ? ?//錯誤C#指針定義
? ? struct ServiceStatus?
? ? {?
? ? ? int State;?
? ? ? string Description; ?// 引用類型 ??
? ? }
? ? ServiceStatus* pStatus ? //錯誤指針定義
?
3. C#中指針賦值
? ? 在C#里,引用類型也叫Movable類型,由于垃圾回收器和內存布局優化,在堆上分配的內存是不固定的,會被挪動。所不 ??
? ? 能將堆上分配的地址賦值給C#指針, 如果能將堆上地址固定,就沒有問題了,那么如何固定引用類型?C#中有一個關鍵字?
? ? fixed可以用在此處。
? ? byte* pData = &bytes[0]; ?// 編譯錯誤,數組在C#里是引用類型,屬于movable
? ? byte[] bytes = new byte[24];?
? ? fixed (byte* pData = &bytes) ? //正確,使用fixed固定?
? ? {?
? ? ? ?// ...?
? ? }
? ? 當然,使用fixed不是沒有代價的,它會使內存出現碎片。
========
C# 指針之美
http://www.cnblogs.com/xiaotie/archive/2012/07/06/2579853.html? ? 將C#圖像庫的基礎部分開源了(https://github.com/xiaotie/GebImage)。這個庫比較簡單,且離成熟還有一段距離,但它是一種新的開發模式的探索:以指針和非托管內存為主的C#程序開發。
? ? 我許多項目都是在這個庫基礎上的開發,實戰證明了它的有效。從今天起,將斷斷續續(太忙了)的寫一系列文章來介紹這種開發方法,介紹基于此的圖像編程。本文便是第一篇。
? ? 以指針和非托管內存為主的C#程序開發,無論對.Net程序員來說,還是對傳統的C/C++程序員來說,均屬異類。然而這種方法在很多場景下是非常有效的,尤其是圖像編程,所謂談笑間,檣櫓灰飛煙滅,不外如是。
? ? 既有C/C++的高性能,又能直接管理內存不給GC帶來壓力,同時又擁有.net開發的大部分優勢,可以快速迭代,何樂而不為呢?
一、簡潔優美的代碼
? ? 本來初稿這節寫了好幾百字,將C#指針開發與C/C++開發,Java開發、D語言開發等進行對比,闡述理念。不過現在覺得,闡述一個新事物,沒有比用例子更直接的了。
? ? 例子:打開一張圖像,先將它轉化為灰度圖像,再進行二值化(變成黑白圖像),然后進行染色,將白色的像素變成紅色。以上每一個過程都彈出窗體顯示出來。
? ? 代碼截圖更有視覺沖擊力:
?
? ? 像詩歌一樣簡潔和優美,這就是孤的代碼。具備C/C++的高性能和C#的行云流水,同時又有IDE的強大生產力相助,說這些話已屬多余,看見這樣的代碼,更應該想到的是:妹紙,今天工作全部搞定,現在有空嗎,哥來接你。
? ? 這才是工作,這才是生活。留下時間,看看書,看看漫畫,玩玩樂樂。最近在看《偷星九月天》,就拿滄殿來測試這段程序吧:
改編
? ? 程序員A:那幫孫子又新提了幾百條需求,老大,你要帶我們突圍嗎?
? ? 程序員B:不是突圍,是殺光它們!
?
? ? ?下面,請跟隨我,來一段短程探險吧。
? ? ?(本文中的代碼可在 https://github.com/xiaotie/GebImage/tree/develop 處下載。打包下載地址見 集異璧圖像與視覺分析庫 )
二、C# 指針基礎
? ? 在C#中使用指針,需要在項目屬性中選中“Allow unsafe code”:
? ? 接著,還需要在使用指針的代碼的上下文中使用unsafe關鍵字,表明這是一段unsafe代碼。
? ? 可以用unsafe { ?} 將代碼圍住,如:
unsafe{new ImageArgb32(path).ShowDialog("原始圖像").ToGrayscaleImage().ShowDialog("灰度圖像").ApplyOtsuThreshold().ShowDialog("二值化圖像").ToImageArgb32().ForEach((Argb32* p) => { if (p->Red == 255) *p = Argb32.RED; }).ShowDialog("染色");}
? ? 也可在方法或屬性上加入unsafe關鍵字,如:
? ? ?private unsafe void btnSubmit_Click(object sender, EventArgs e)
?
? ? 也可在class或struct 上加上unsafe 關鍵字,如:
? ? ?public partial unsafe class FrmDemo1 : Form
? ? 指針配合fixed關鍵字可以操作托管堆上的值類型,如:
public unsafe class Person{public int Age;public void SetAge(int age){fixed (int* p = &Age){*p = age;}}}
? ? 指針可以操作棧上的值類型,如:
int age = 0;int* p = &age;*p = 20;MessageBox.Show(p->ToString());
?
? ? 指針也可以操作非托管堆上的內存,如:
IntPtr handle = System.Runtime.InteropServices.Marshal.AllocHGlobal(4);Int32* p = (Int32*)handle;*p = 20;MessageBox.Show(p->ToString());System.Runtime.InteropServices.Marshal.FreeHGlobal(handle);
? ? System.Runtime.InteropServices.Marshal.AllocHGlobal 用來從非托管堆上分配內存。System.Runtime.InteropServices.Marshal.FreeHGlobal(handle)用來釋放從非托管對上分配的內存。這樣我們就可以避開GC,自己管理內存了。
三、幾種常用用法
? ? 1、使用Dispose模式管理非托管內存
? ? 如果使用非托管內存,建議用Dispose模式來管理內存,這樣做有以下好處: 可以手動dispose來釋放內存;可以使用using 關鍵字開管理內存;即使不釋放,當Dispose對象被GC回收時,也會收回內存。
? ? 下面是Dispose模式的簡單例子:
View Code
? ? 使用:
using (UnmanagedMemory memory = new UnmanagedMemory(10)){int* p = (int*)memory.Handle;*p = 20;MessageBox.Show(p->ToString());}
? ? 2、使用 stackalloc 在棧中分配內存
? ? C# 提供了stackalloc 關鍵字可以直接在棧中分配內存,一般情況下,使用棧內存會比使用堆內存速度快,且棧內存不用擔心內存泄漏。下面是例子:
int* p = stackalloc int[10];for (int i = 0; i < 10; i++){p[i] = 2 * i + 2;}MessageBox.Show(p[9].ToString());
? ? 3、模擬C中的union(聯合體)類型
? ? ?使用 StructLayout 可以模擬C中的union:
[StructLayout(LayoutKind.Explicit)]public struct Argb32{[FieldOffset(0)]public Byte Blue;[FieldOffset(1)]public Byte Green;[FieldOffset(2)]public Byte Red;[FieldOffset(3)]public Byte Alpha;[FieldOffset(0)]public Int32 IntVal;}
? ? 這個和指針無關,非unsafe環境下也可使用,有很多用途,比如,序列化和反序列化,求hash值 ……
四、C# 指針操作的幾個缺點
? ? C# 指針操作的缺點也不少。下面一一道來。
? ? 缺點1:只能用來操作值類型
? ? .Net中,引用類型的內存管理全部是由GC代勞,無法取得其地址,因此,無法用指針來操作引用類型。所以,C#中指針操作受到值類型的限制,其中,最主要的一點就是:值類型無法繼承。
? ? 這一點看起來是致命的,其實不然。首先,需要用到指針來提高性能的地方,其類型是很少變動的。其次,在OO編程中有個名言:組合優于繼承。使用組合,我們可以解決很多需要繼承的地方。第三,最后,我們還可以使用引用類型來對值類型打包,進行繼承,權衡兩者的比重來完成任務。
? ? 缺點2:泛型不支持指針類型
? ? C# 中泛型不支持指針類型。這是個很大的限制,在后面的篇幅中,我會引入模板機制來克服這個問題。同理,迭代器也不支持指針,因此,我們需要自己實現迭代機制。
? ? 缺點3:沒有函數指針
? ? 幸運的是,C# 中有delegate,delegate 支持支持指針類型,lambda 表達式也支持指針。后面會詳細講解。
五、引入模板機制
? ? 沒有泛型,但是我們可以模擬出一套類似C++的模板機制出來,進行代碼復用。這里大量的用到了C#的語法糖和IDE的支持。
? ? 先介紹原理:
? ? partial 關鍵字讓我們可以將一個類的代碼分在多個文件,那么可以這樣分:第一個文件是我們自己寫的代碼,第二個文件用來描述模板,第三個文件,用來根據模板自動生成代碼。
? ? 三個文件這樣取名字的:
? ? XXXClassHelper 是模板定義文件,XXXClassHelper_Csmacro.cs 是自動生成的模板實現代碼。
? ? ClassHelper文件的例子:
namespace Geb.Image
{
? ? using TPixel = Argb32;
? ? using TCache = System.Int32;
? ? using TKernel = System.Int32;
? ? using TImage = Geb.Image.ImageArgb32;
? ? using TChannel = System.Byte;
? ? public static partial class ImageArgb32ClassHelper
? ? {
? ? ? ? #region include "ImageClassHelper_Template.cs"
? ? ? ? #endregion
? ? }
? ? public partial class ImageArgb32
? ? {
? ? ? ? #region include "Image_Template.cs"
? ? ? ? #endregion
? ? ? ? #region include "Image_Paramid_Argb_Templete.cs"
? ? ? ? #endregion
? ? }
? ? public partial struct Argb32
? ? {
? ? ? ? #region include "TPixel_Template.cs"
? ? ? ? #endregion
? ? }
}
? ? 這里用到了using 語法糖。using 關鍵字,可以為一個類型取別名。使用 VS 的 #region 來定義所使用的模板文件的位置。上面這個文件中,引用了4個模板文件:ImageClassHelper_Template.cs,Image_Template.cs,Image_Paramid_Argb_Templete.cs 和 TPixel_Template.cs。
? ? 只看其中的一個模板文件 ?Image_Template.cs:
?using TPixel = System.Byte;
?using TCache = System.Int32;
?using TKernel = System.Int32;
?
?using System;
?using System.Collections.Generic;
?using System.Text;
?
?namespace Geb.Image.Hidden
?{
? ? ?public abstract class Image_Template : UnmanagedImage<TPixel>
? ? ?{
? ? ? ? ?private Image_Template()
? ? ? ? ? ? ?: base(1,1)
? ? ? ? ?{
? ? ? ? ? ? ?throw new NotImplementedException();
? ? ? ? ?}
?
? ? ? ? ?#region mixin
?
? ? ? ? ?public unsafe TPixel* Start { get { return (TPixel*)this.StartIntPtr; } }
?
? ? ? ? ?public unsafe TPixel this[int index]
? ? ? ? ?{
? ? ? ? ? ? ?get
? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?return Start[index];
? ? ? ? ? ? ?}
? ? ? ? ? ? ?set
? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?Start[index] = value;
? ? ? ? ? ? ?}
? ? ? ? ?}? ?
? ?……?
?
? ? ? ? ?#endregion
? ? ?}
?}
? ? 這個模板文件是編譯通過的。也使用了 using 關鍵字來對使用的類型取別名,同時,在代碼中,有一段用 #region mixin 和 #endregion 環繞的代碼。只需要寫一個工具,將模板文件中 #region mixin 和 #endregion 環繞的代碼提取出來,替換到模板定義中 #region include "Image_Template.cs" 和 #endregion 之間,生成第三個文件 ClassHelper_Csmacro.cs 即可實現模板機制。由于都使用了 using 關鍵字對類型取別名,因此,ClassHelper_Csmacro.cs 文件也是可以編譯通過的。在不同的模板定義中,令同樣的符號來代表不同的類型,實現了模板代碼的公用。
? ? 上面機制可以全部自動化。Csmacro 是我寫的一個工具,可以完成上面的過程。將它放在系統路徑下,然后在項目的build event中添加pre-build 指令即可。Csmacro 程序在代碼包的lib的目錄下。
? ? 如此實裝,我們就有模板用了!一切自動化,就好像內置的一樣。強類型、有編譯器進行類型約束,減少出錯的可能。調試也很容易,就和調試普通的C#代碼一樣,不存在C++中的模板的難調試問題。缺點嘛,就是沒有C++中模板的語法優美,但是,也看的過去,至少比C中的宏好看多了是吧。
? ? 參照上面對模板的實現,完全可以定義出一套C#的宏出來。沒這樣做,是因為沒這個需求。
? ? 下面是一個完整的例子,為 Person 類和 Cat 類添加模板擴展方法(非擴展方法也可類似添加),由于這個方法有指針,無法用泛型實現:
?void SetAge(this T item, ?int* age)
? ? 首先,建一個可編譯通過的模板類 Template.cs:
?namespace Introduce.Hide
?{? ? ?using T = Person;
?
? ? ?public static class Template
? ? ?{
? ? ? ? ?#region mixin
?
? ? ? ? ?public static unsafe void SetAge(this T item, ?int* age)
? ? ? ? ?{
? ? ? ? ? ? ?item.Age = *age;
? ? ? ? ?}
?
? ? ? ? ?#endregion
? ? ?}
?}
?
? ? 我在命名空間中加入了 Hide,只要不引用這個命名空間,這個擴展方法不會出現對程序產生干擾。
? ? 接著,建立 PersonClassHelper.cs 文件:
?namespace Introduce
?{
? ? ?using T = Person;
?
? ? ?public static partial class PersonClassHelper
? ? ?{
? ? ? ? ?#region include "Template.cs"
? ? ? ? ?#endregion?
? ? ?}
?}
? ? 建立 CatClassHelper.cs 文件:
?namespace Introduce
?{
? ? ?using T = Cat;
?
? ? ?public static partial class CatClassHelper
? ? ?{
? ? ? ? ?#region include "Template.cs"
? ? ? ? ?#endregion
? ? ?}
?}
?
? ? 為了節省篇幅,我省略了命名空間的引用,實際代碼中是有命名空間的引用的。下載包里包含了全部的代碼。
? ? 接下來,編譯一下,哈哈,編譯通過。
? ? 且慢,怎么看不到編譯生成的兩個 Csmacro.cs 文件呢?
? ? 這兩個文件已經生成了,需要手動將它們添加到項目中,只用添加一次即可。添加進來,再編譯一下,哈哈,通過。
? ? 這個例子雖小,可不要小看模板啊,在Geb.Image庫里,大量使用了模板:
? ? 有了模板,只用維護公共代碼。
六、迭代器
? ? 下面來實現迭代器。這里,要放棄使用foreach,返回古老的迭代器模式,來訪問圖像的每一個像素:
? ? public unsafe struct ItArgb32Old
? ? {
? ? ? ? public unsafe Argb32* Current;
? ? ? ? public unsafe Argb32* End;
? ? ? ? public unsafe Argb32* Next()
? ? ? ? {
? ? ? ? ? ? if (Current < End) return Current ++;
? ? ? ? ? ? else return null;
? ? ? ? }
? ? }
? ? public static class ImageArgb32Helper
? ? {
? ? ? ? public unsafe static ItArgb32Old CreateItorOld(this ImageArgb32 img)
? ? ? ? {
? ? ? ? ? ? ItArgb32Old itor = new ItArgb32Old();
? ? ? ? ? ? itor.Current = img.Start;
? ? ? ? ? ? itor.End = img.Start + img.Length;
? ? ? ? ? ? return itor;
? ? ? ? }
? ? }
? ? 不幸的是,測試性能,這個迭代器比單純的while循環慢很多。對一個100萬像素的圖像,將其每一個像素值的Red分量設為200,循環100遍,使用迭代器在我的電腦上耗時242 ms,直接使用循環耗時 72 ms。我測試了很多種方案,均未得到和直接循環性能近似的迭代器實現方案。
? ? 沒有辦法,只好對迭代器來打折了,只進行部分抽象(這已經不能算迭代器了,但這里仍沿用這個名稱):
? ? ?public unsafe struct ItArgb32
? ? ?{
? ? ? ? ?public unsafe Argb32* Start;
? ? ? ? ?public unsafe Argb32* End;
?
? ? ? ? ?public int Step(Argb32* ptr)
? ? ? ? ?{
? ? ? ? ? ? ?return 1;
? ? ? ? ?}
? ? ?}
?
? ? 產生迭代器的代碼:
? ? ?public unsafe static ItArgb32 CreateItor(this ImageArgb32 img)
? ? ?{
? ? ? ? ?ItArgb32 itor = new ItArgb32();
? ? ? ? ?itor.Start = img.Start;
? ? ? ? ?itor.End = img.Start + img.Length;
? ? ? ? ?return itor;
? ? ?}
? ? ?使用:
? ? ?ItArgb32 itor = img.CreateItor();
? ? ?for (Argb32* p = itor.Start; p < itor.End; p+= itor.Step(p))
? ? ?{
? ? ? ? ?p->Red = 200;
? ? ?}
?
? ? 測試性能和直接循環性能幾乎一樣。有人可能要問,你這樣有什么優勢?和for循環有什么區別?
? ? 這個例子中當然看不出優勢,換個例子就可以看出來了。
? ? 在圖像編程中,有 ROI(Region of Interest,感興趣區域)的概念。比如,在下面這張女王出場的畫面中,假設我們只對她的頭部感興趣(ROI區域),只對該區域進行處理(標注為紅色區域)。
? ? 對ROI區域創建一個迭代器,用來迭代ROI中的每一行:
? ? public unsafe struct ItRoiArgb32
? ? {
? ? ? ? public unsafe Argb32* Start;
? ? ? ? public unsafe Argb32* End;
? ? ? ? public int Width;
? ? ? ? public int RoiWidth;
? ? ? ? public int Step(Argb32* ptr)
? ? ? ? {
? ? ? ? ? ? return Width;
? ? ? ? }
? ? ? ? public ItArgb32 Itor(Argb32* p)
? ? ? ? {
? ? ? ? ? ? ItArgb32 it = new ItArgb32();
? ? ? ? ? ? it.Start = p;
? ? ? ? ? ? it.End = p + RoiWidth;
? ? ? ? ? ? return it;
? ? ? ? }
? ? }
?
? ? 這個ROI迭代器又可以產生一個ItArgb32迭代器,來迭代該行中的像素。
? ? 產生ROI迭代器的代碼如下,為了簡化代碼,我這里沒有進行ROI的驗證:
? ? ? ? public unsafe static ItRoiArgb32 CreateRoiItor(this ImageArgb32 img,
? ? ? ? ? ? int x, int y, int roiWidth, int roiHeight)
? ? ? ? {
? ? ? ? ? ? ItRoiArgb32 itor = new ItRoiArgb32();
? ? ? ? ? ? itor.Width = img.Width;
? ? ? ? ? ? itor.RoiWidth = roiWidth;
? ? ? ? ? ? itor.Start = img.Start + img.Width * y + x;
? ? ? ? ? ? itor.End = itor.Start + img.Width * roiHeight;
? ? ? ? ? ? return itor;
? ? ? ? }
? ? 性能測試表明,使用ROI迭代器進行迭代和直接進行循環,性能一致。
? ? 為一副圖像添加ROI字段,設置ROI值來控制不同的處理區域,然后用ROI迭代器進行迭代,比直接使用循環要方便得多。
七、風情萬種的Lambda表達式
? ? 接下來,來看看C#指針最有風情的一面——Lambda表達式。
? ? C# 里 delegate 支持指針,下面這種寫法是沒有問題的:
?void ActionOnPixel(TPixel* p);
? ? 對于圖像處理,我定義了許多擴展方法,ForEach是其中的一種,下面是它的模板定義:
? ? ? ? public unsafe static UnmanagedImage<TPixel> ForEach(this UnmanagedImage<TPixel> src, ActionOnPixel handler)
? ? ? ? {? ? ? ? ? ? TPixel* start = (TPixel*)src.StartIntPtr;
? ? ? ? ? ? TPixel* end = start + src.Length;
? ? ? ? ? ? while (start != end)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? handler(start);
? ? ? ? ? ? ? ? ++start;
? ? ? ? ? ? }
? ? ? ? ? ? return src;
? ? ? ? }
? ? 讓我們用lambda表達式對圖像迭代,將每像素的Red分量設為200吧,一行代碼搞定:
img.ForEach((Argb32* p) => { p->Red = 200; });
? ? 用ForEach測試,對100萬像素的圖像設置Red通道值為200,循環100次,我的測試結果是 400 ms,約是直接循環的 4-5 倍??梢娺@是個性能不高的操作(其實也夠高了,100萬象素,循環100遍,耗時400ms),可以在對性能要求不是特別高時使用。
八、與C/C++的比較
? ? 我測試了很多場景,C# 下指針性能約是 C/C++ 的 70-80%,性能差距,可以忽略。
? ? 相對于C/C++來說,C#無法直接操作硬件是其遺憾,這種情況,可以使用C/C++寫段小程序來彌補,不過,我還沒遇到這種場景。很多情況都可以P/Invoke解決。
? ? 做圖像的話,很多時候需要使用顯卡加速,如使用CUDA或OpenCL,幸運的是,C#也可以直接寫CUDA或OpenCL代碼,但是功能可能會受到所用的庫的限制。也可以用傳統方式寫CUDA或OpenCL代碼,再P/Invoke調用。如果用傳統的C/C++開發的話,也需要做同樣的工作。
和C比較:
? ? 這套方案比C的抽象程度高,我們有模板,有lambda表達式,還有一大票的語法糖。在類庫上,比C的類庫完善的多。我們還有反射,有命名空間等等一大票的東西。
和C++比較:
? ? 這套方案的抽象程度比C++要低一些。畢竟,值類型無法繼承,模板機制比C++ 差一點。但是在生產力上比C++要高很多。拋開C++那一大票陷阱不說,以秒計算的編譯速度就夠讓C++程序員流口水的。當我們在咖啡館里約會喝咖啡時,C++程序員還正端著一杯咖啡坐在電腦前等待程序編譯結束。
九、接下來的工作
? ? 接下來的工作主要有兩個:
? ? 內聯工具:C# 的內聯還不夠強大。需要一個內聯工具,對想要內聯的方法使用特性標記一下,在編譯結束后,在IL代碼層面內聯。
? ? 翻譯工具:移動開發是個痛。如何將C#的代碼翻譯成C/C++的代碼,在缺乏.Net的運行時下運行?
? ? 這兩個工作都不緊要。C#內聯效果不好的地方(這種情況很少),可以手動內聯。至于移動開發嘛,在哥的一云三端大計中,C# 的定位是云圖像開發(C#+CUDA),三端中,桌面運用是用C#和Flash開發,Web和移動應用使用Flash開發,沒有C#的事情。
? ? C/C++ 呢?更沒有它們的位置啦!不對,還是有的。用它們來開發Flash應用的核心算法!夠另類吧!
========
在c#中使用指針
http://blog.csdn.net/susan19890313/article/details/7365996版權聲明:本文為博主原創文章,未經博主允許不得轉載。
? ?如果想在c#中使用指針,首先對項目進行配置:在解決方案資源管理器中右擊項目名選擇屬性(或在項目菜單中選擇consoleApplication屬性(consoleApplication為項名)),在生成選項卡中 選中“允許不安全代碼”,如下圖:
? ? ? 然后將有關指針,地址的操作放在unsafe語句塊中。使用unsafe關鍵字是來告訴編譯器下面的代碼是不安全的。
unsafe關鍵字的使用:
?(1)放在函數前,修飾函數,說明在函數內部或函數的形參涉及到指針操作
? ? ? ?unsafe static void FastCopy(byte[] src, byte[] dst, int count)
{
? ? // Unsafe context: can use pointers here.
}
? ? ? 不安全上下文的范圍從參數列表擴展到方法的結尾,因此指針作為函數的參數時須使用unsafe關鍵字
? ? ? ? ?unsafe static void FastCopy ( byte* ps, byte* pd, int count ) {...}
(2)將有關指針的操作放在由unsafe聲明的不安全塊中
? ? ? ?unsafe
? ? ? { ??
? ? ? ? ?// Unsafe context: can use pointers here.?
? ? ? }
?
示例:
[csharp] view plain copy
using System; ?
using System.Collections.Generic; ?
using System.Linq; ?
using System.Text; ?
??
namespace ConsoleApplication1 ?
{ ? ?
? ? class Program ?
? ? { ???
? ? ? ? ??
? ? ? ? static void Main(string[] args) ?
? ? ? ? { ?
??
? ? ? ? ? ? int i = 1; ?
? ? ? ? ? ? unsafe ??
? ? ? ? ? ? { ??
? ? ? ? ? ? ? ? Increment(&i); //將函數調用放在unsafe中是因為實參是指針類型 ?
? ? ? ? ? ? } ?
? ? ? ? ? ? ??
? ? ? ? ? ? Console.WriteLine(i+"\n"); ?
??
? ? ? ? ? ? //演示如何使用指針來操作字符串 ?
? ? ? ? ? ? string s = "Code Project is cool"; ?
? ? ? ? ? ? Console.Write("the original string : "); ?
? ? ? ? ? ? Console.WriteLine("{0}\n", s); ?
??
? ? ? ? ? ? char[] b = new char[100]; ?
? ? ? ? ? ? s.CopyTo(0, b, 0, 20);//public void CopyTo (int sourceIndex,char[] destination,int destinationIndex,int count) ?
? ? ? ? ? ? //將指定數目的字符從此實例中的指定位置復制到 Unicode 字符數組中的指定位置。 ?
??
? ? ? ? ? ? Console.Write("the encoded string : "); ?
??
? ? ? ? ? ? unsafe ??
? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? fixed (char* p = b) ??
? ? ? ? ? ? ? ? { ??
? ? ? ? ? ? ? ? ? ? NEncodeDecode(p); ??
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? } ?
? ? ? ? ? ? for (int t = 0; t < 20; t++) ?
? ? ? ? ? ? ? ? Console.Write(b[t]); ?
? ? ? ? ? ? Console.WriteLine("\n"); ?
??
? ? ? ? ? ? Console.Write("the decoded string : "); ?
? ? ? ? ? ? unsafe ?
? ? ? ? ? ? { ??
? ? ? ? ? ? ? ? fixed (char* p = b) ??
? ? ? ? ? ? ? ? { ??
? ? ? ? ? ? ? ? ? ? NEncodeDecode(p); ??
? ? ? ? ? ? ? ? } ??
? ? ? ? ? ? } ?
? ? ? ? ? ? for (int t = 0; t < 20; t++) ?
? ? ? ? ? ? ? ? Console.Write(b[t]); ?
? ? ? ? ? ? Console.WriteLine(); ?
??
? ? ? ? } ? ?
??
? ? ? ? unsafe public static void Increment(int* p) ?
? ? ? ? { ?
??
? ? ? ? ? ? *p = *p + 1; ?
? ? ? ? } ?
??
? ? ? ? unsafe public static void NEncodeDecode(char* s) ?
? ? ? ? {//將一段字符串通過異或運算進行編碼解碼的操作。如果您將一段字符串送入這個函數這個字符串將會被編碼, ?
? ? ? ? //如果您將一段已經編碼的字符送入這個函數,這段字符串將會被解碼。 ?
? ? ? ? ? ? int w; ?
? ? ? ? ? ? for (int y = 0; y < 20; y++) ?
? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? w = (int)*(s + y); ?
? ? ? ? ? ? ? ? w = w ^ 5;//^為按位異或。 ?
? ? ? ? ? ? ? ? *(s + y) = (char)w; ?
? ? ? ? ? ? } ?
??
? ? ? ? } ?
? ? } ?
} ?
輸出結果:
?
關鍵字fixed簡介:
? ?fixed語句只能出現在不安全的上下文中。
? ?C# 編譯器只允許在 fixed 語句中分配指向托管變量的指針,無法修改在 fixed 語句中初始化的指針
? ? fixed 語句禁止垃圾回收器重定位可移動的變量。 當你在語句或函數之前使用fixed時,你是在告訴.Net平臺的垃圾回收器,在這個語句或函數執行完畢前,不得回收其所占的內存空間。
?示例:
(1)
// assume class Point { public int x, y; } ?pt is a managed variable, subject to garbage collection.
Point pt = new Point();
// Using fixed allows the address of pt members to be taken, and "pins" pt so it isn't relocated.(pin ?v 固定住)
fixed ( int* p = &pt.x )
{
? ? *p = 1;
}
(2)用數組或字符串的地址初始化指針:
? ? ? ? ? fixed (int* p = arr) ... ?// equivalent to p = &arr[0]
? ? ? ? ? fixed (char* p = str) ... // equivalent to p = &str[0]
(3)只要指針的類型相同,就可以初始化多個指針:
? ? ? ? fixed (byte* ps = srcarray,pd = dstarray) {...}
? ? ? 要初始化不同類型的指針,只需嵌套 fixed 語句:
? ? ? ? ? ? ? ? fixed (int* p1 = &p.x)
? ?{
? ? ? fixed (double* p2 = &array[5])
? ? { ? ? ??
? ? ? // Do something with p1 and p2.
? ?}
? }?
========
C#中指針使用總結
http://www.cnblogs.com/jasonlny/p/3203439.htmlC#為了類型安全,默認并不支持指針。但是也并不是說C#不支持指針,我們可以使用unsafe關鍵詞,開啟不安全代碼(unsafe code)開發模式。在不安全模式下,我們可以直接操作內存,這樣就可以使用指針了。在不安全模式下,CLR并不檢測unsafe代碼的安全,而是直接執行代碼。unsafe代碼的安全需要開發人員自行檢測。
一、Vs2010中開啟unsafe code 的方式
在方法、類、代碼塊中使用unsafe關鍵詞,如:
unsafe static void Main(string[] args){ //代碼}
unsafe
{
//代碼塊
}
然后再項目上點擊鼠標右鍵,選擇“屬性”,在“生成”選項卡中選中“允許不安全代碼”
二、C#可以定義為指針的類型有
sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool,
struct(結構體),結構體中只能包括非托管類型。
三、C#指針操作符
操作符 說明
* 取值運算符
?
& 取址運算符
-> 通過指針處理結構體中的數據(獲取或賦值)
++與– 指針增、減操作
fixed 用戶暫時固定托管代碼中引用類型的位置。
Stackallc 分配內存
例如分配內存
char* cptr = stackalloc char[26];
for (int i = 0; i < 26;i++ ){
? ? cptr[i] = (char) (i+65);
}
for (int i = 0; i < 26;i++ )
{
? ? Console.WriteLine(string.Format("{0}:{1}",(int)&cptr[i],cptr[i]));
}
至于釋放內存,我在msdn上搜索了一下,c#并沒有提供釋放內存的函數。而msdn給的解釋是:分配的內存會在方法結束后自動釋放。
fixed的應用會在下面的類與指針中做說明。
四、C#指針的定義
定義指針 說明
int* p 整形指針
int** p 指向整形指針的指針
char* c 指向字符的指針
int*[] arr 整形一維數組指針
五、指針的使用
1.整形指針的使用
int i=10;
int* iptr = &i; ? ?//將i的地址賦值給iptr
Console.WriteLine((int)&iptr); ?//取iptr得地址
Console.WriteLine(*iptr); ? ? //取iptr指向的值
2.結構體指針
struct Location
{
? ? public int X;
? ? public int Y;
}
unsafe static void Main(string[] args)
{
? ? Location location;
? ? location.X = 10;
? ? location.Y = 5;
? ? Location* lptr = &location;
? ? Console.WriteLine(string.Format("location 地址{0},lptr地址{1},lptr值{2}",(int)&location,(int)lptr,*lptr));
? ? Console.WriteLine(string.Format("location.x的地址{0},location.x的值{1}",(int)&(lptr->X),lptr->X));
? ? Console.WriteLine(string.Format("location.y的地址{0},location.y的值{1}", (int)&(lptr->Y), lptr->Y));
?}
以上代碼輸出結構體的地址和值。我們在操作地址時,可以直接看到結構體的內存分配。
3.指針與參數
public static unsafe int* Add(int* x,int* y)
{
? ? int sum = *x + *y;
? ? return ∑
}
int i = 2, j = 3;
Console.WriteLine(*Add(&i,&j));
4.類與指針,因為類是托管類型,我們知道類受到“垃圾收集”的影響,它的內存地址是不固定的。而且類是引用類型,是不能聲明為指針類型的。而指針分配內存后,不受“垃圾收集”影響,地址是固定的。所以為了使用類中的數據,我們需要臨時固定類的地址。這就用到fixed關鍵詞,用fixed后,就可以操作類中的值類型了。
class People
{
? ? public int Age; ? //值類型,不可以是屬性
? ? public void ShowAge()
? ? {
? ? ? ? Console.WriteLine(Age);
? ? }
}
People people = new People();
people.Age = 10;
fixed(int* agePtr=&people.Age)
{
? ? *agePtr += 1;
}
people.ShowAge(); ?//11
通過以上的方法,我們可以操作值類型,也可以獲得值類型的地址。但如何獲取類的內存地址?我們可以使用GCHandle,來自System.Runtime.InteropServices命名空間。GCHandle提供從非托管內存訪問托管對象的方法。如下:
object p = new People();
GCHandle h = GCHandle.Alloc(p, GCHandleType.Pinned);
IntPtr addr = h.AddrOfPinnedObject();?
Console.WriteLine(addr.ToString());
h.Free();
六、C#中使用指針的總結
1.引用類型不能定義為指針
2.msdn上說enum可以定義為指針,可是我真不知道它的用處是什么。所以在指針的類型中并沒有出現enum類型。
3.c#中的指針操作遠遠不如c/c++,如果想學習指針的話,還是用c/c++
4.微軟并不推薦使用unsafe code模式,也不推薦使用指針。在msdn官方文檔中,唯一一句贊美C#指針的話就是“合理的使用指針,可以提高程序的執行速度”。但是什么是“合理的使用”?我下載了msdn上的幾個關于C#指針的實例代碼,發現用的最多的是調用api函數,在api函數中,有大量的指針參數。
5.fixed的使用可能產生存儲碎片,因為它們不能移動。如果確實需要固定對象,固定對象的時間應該越短越好。
6.可以使我們了解非托管類型的內存分配。
========
對于.Net中C#指針的研究
? ? ? 在C/C++中,對于指針的使用是很普遍的,可以這么說,如果沒有指針的運用,都不知道程序如何來寫。在.Net中,同樣也是可以使用指針的,不過必須通過開啟不安全的代碼來使用。在默認情況下,新建的項目是不允許使用不安全的代碼,這樣,就必須通過設置項目來開啟使用,通過設置項目“屬性”的“生成”來達到:
image
勾選“允許不安全代碼”的選項就OK了。
1、unsafe
要使用指針,還不必須在你的方法體或者是某個作用域中添加unsafe關鍵字,如:
unsafe static void Main(string[] args)?
{?
// 不安全代碼編寫
}
或者:
unsafe
{
// 不安全代碼編寫
}
?
2、stackalloc
? ? ? 關鍵字stackalloc是用于申請棧內存。stackalloc類似于C庫中的_alloca。通過使用stackalloc可以自動啟用CLR中的緩沖區溢出檢測功能。當函數執行完畢后,內存會被自動回收。
? ? ? 使用它的目的通過它來實現對棧內存的使用,從而減少對堆的使用,減少系統開銷。
如下參考程序:
int* array = stackalloc int[10];?
for (int index = 0; index < 10; index++)?
{?
? ? array[index] = index;?
}?
for (int index = 0; index < 10; index++)?
{?
? ? Console.WriteLine(array[index].ToString());?
}?
代替了程序:int[] array = new int[10]; ?等同于System.Array的數據存在堆中,這里通過指針的方式減少系統的開銷。
?
3、IntPtr 和 ref
IntPtr是.Net提供的托管指針,ref是C#語言中的關鍵字,和IntPtr沒有關系,盡管它們使用起來很類似。區別在于,使用ref可以引用Class,共同點它們都可以引用值類型,包括Struct結構體等等。
?
接著,編寫一個Struct結構體:
? ? [StructLayout(LayoutKind.Sequential)] ?
? ? public struct ST_TERMPARA?
? ? {?
? ? ? ? [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]?
? ? ? ? public byte[] szAcqID_n_9F01; ? ? ? ? ? ? ? ?/**<(TERM)收單行標識*/ ? ?
? ? ? ? [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]?
? ? ? ? public byte[] szAddTermCap_b_9F40; ? ? ? ?/**<(TERM)附加終端性能*/ ? ?
? ? ? ? [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]?
? ? ? ? public?
========
總結
以上是生活随笔為你收集整理的C#指针使用学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windbg !htrace 学习总结
- 下一篇: C# 泛型类型参数的约束