C# 通過不安全代碼看內存加載

(注:本篇用點長,有點繞,耐心瀏覽)

C#中類型分為值類型和引用類型,值類型存儲在堆棧中,是棧結構,先進后出,引用類型存儲在托管堆中。接下來用不安全代碼的地址,來看一下值類型和引用類型的存儲。

項目文件

C#中使用不安全代碼需要在項目文件中添加AllowUnsafeBlocks配置。

<Project?Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net7.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><AllowUnsafeBlocks>true</AllowUnsafeBlocks></PropertyGroup>
</Project>

所有的測試案例都是定義兩個特定類型的變量,然后查看它的內存地址,然后進行調用一個方法進行相加運算,然后分別在方法內輸出變量和結查內存地址,最后返回主方法后變量的內存地址。

值類型:

static void TestDouble()
{var v1 = 1.00001d;var v2 = 2.00002d;Console.WriteLine("TestDouble v1 " + (long)&v1);Console.WriteLine("TestDouble v2 " + (long)&v2);Console.WriteLine("TestDouble v2-v1 " + ((long)&v2 - (long)&v1));var v3 = Add(v1, v2);Console.WriteLine("TestDouble v3 " + (long)&v3);Console.WriteLine("TestDouble v3-v2 " + ((long)&v3 - (long)&v2));Console.WriteLine("TestDouble v3-v1 " + ((long)&v3 - (long)&v1));
}static double Add(double v1, double v2)
{Console.WriteLine("Add v1 " + (long)&v1);Console.WriteLine("Add v2 " + (long)&v2);Console.WriteLine("Add v2-v1 " + ((long)&v2 - (long)&v1));var v3 = v1 + v2;Console.WriteLine("Add v3 " + (long)&v3);Console.WriteLine("Add v3-v2 " + ((long)&v3 - (long)&v2));Console.WriteLine("Add v3-v1 " + ((long)&v3 - (long)&v1));return v3;
}

25e8e1e5ff719370787338ea8573e084.png

v1的所在內存地址大于v2,最后運算完的v3是最小的,我們可以想象,v1放在棧的最后面,地址最大,然后放v2,最后放v3。回收時的順序是反回來的。那么Add方法里,v2地址最大,但比TestDouble都要小,說明進棧要晚一些,接下來是v1進棧,最后是v3進棧,不過TestDouble里的每個變量都相差8,但方法里的就不是了,這是因為方法參數,返回值等信息,還要占一些內存空間。還有TestDouble的v3為什么能和v2相差8?不是有Add方法嗎?原因是Add調用完后都出棧了,所以TestDouble的v3和v2是相鄰的。

自定義結構體:

struct TestStruct
{public TestStruct(){i = 100;}public long i;
}
static void TestTestStruct()
{var v1 = new TestStruct();Console.WriteLine("TestStruct原v1對象地址= " + (long)&v1);var v2 = new TestStruct();Console.WriteLine("TestStruct原v2對象地址= " + (long)&v2);Console.WriteLine("TestStruct v2-v1 " + ((long)&v2 - (long)&v1));var?v3?=?Add(v1,?v2);Console.WriteLine("TestStruct原v3對象地址= " + (long)&v3);Console.WriteLine("TestStruct v3-v2 " + ((long)&v3 - (long)&v2));
}
static TestStruct Add(TestStruct v1, TestStruct v2)
{Console.WriteLine("Add TestStruct v1對象地址= " + (long)&v1);Console.WriteLine("Add TestStruct v2對象地址= " + (long)&v2);Console.WriteLine("Add TestStruct  v2-v1 " + ((long)&v2 - (long)&v1));var v3 = new TestStruct();v3.i = v1.i + v2.i;Console.WriteLine("Add TestStruct v3對象地址" + (long)&v3);Console.WriteLine("Add TestStruct  v3-v2 " + ((long)&v3 - (long)&v2));return v3;
}

f2d3dce39de75449326f4c88ef4bb9cc.png

自定義struct與double類似,本質上double也是用struct定義的。

引用類型string

static void TestString()
{long ad1, ad2, ad3;var v1 = "aaaa";var v2 = "bbbb";fixed (char* p = v1){ad1 = (long)p;Console.WriteLine("TestString v1字符串地址= " + (long)p);}fixed (char* p = v2){ad2 = (long)p;Console.WriteLine("TestString v2字符串地址= " + (long)p);}Console.WriteLine("TestString v2-v1 " + (ad2 - ad1));var v3 = Add(v1, v2);fixed (char* p = v3){ad3 = (long)p;Console.WriteLine("TestString v3字符串地址= " + (long)p);}Console.WriteLine("TestString v3-v2 " + (ad3 - ad2));
}static string Add(string v1, string v2)
{long ad1, ad2, ad3;fixed (char* p = v1){ad1 = (long)p;Console.WriteLine("Add中v1字符串地址= " + (long)p);}fixed (char* p = v2){ad2 = (long)p;Console.WriteLine("Add中v2字符串地址= " + (long)p);}Console.WriteLine("Add中 v2-v1 " + (ad2 - ad1));var v3 = v1 + v2;fixed (char* p = v3){ad3 = (long)p;Console.WriteLine("Add中v3字符串地址= " + (long)p);}Console.WriteLine("Add中 v3-v2 " + (ad3 - ad2));Console.WriteLine("Add中 v3-v1 " + (ad3 - ad1));return v3;
}
static void TestString2()
{var v1 = "aaaa";var v2 = "bbbb";var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("TestString2 v1對象地址= " + (long)h1.AddrOfPinnedObject());var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("TestString2 v2對象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("TestString2 v2-v1 " + ((long)h2.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));var v3 = Add2(v1, v2);var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("TestString2 v3對象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("TestString2 v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));
}
static string Add2(string v1, string v2)
{var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("Add2中的v1對象地址= " + (long)h1.AddrOfPinnedObject());var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("Add2中的v2對象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("Add2 v2-v1 " + ((long)h2.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));var v3 = v1 + v2;var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("Add2中的v3對象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("Add2 v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));Console.WriteLine("Add2 v3-v1 " + ((long)h3.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));return v3;
}

c8263684c4f987330b8be1e849981fe1.png

字符串是引用類型,v1比v2內存地址小,進入Add后,v1和v2與傳入的地址相同,因為是引用類型,Add方法里的v3接著往大走,并且與返回的v3是一個地址,這些沒有問題。

string用了兩種方法,發現兩個方式v1都是aaaa,v2都是bbbb,因為字符串有留用性,所以兩個方法的v1和v2是一樣的;但兩種方式調用了Add后,在Add里的v3都是aaaabbbb,都是拼接,但拼出來的字符串的地址不相同,所以這塊沒有留用。

自定class類型

class?TestClass
{public int i = 100;
}
static void TestTestClass()
{var v1 = new TestClass();var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("TestTestClass v1對象地址= " + (long)h1.AddrOfPinnedObject());var v2 = new TestClass();var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("TestTestClass v2對象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("TestTestClass?v2-v1?"?+?((long)h2.AddrOfPinnedObject()?-?(long)h1.AddrOfPinnedObject()));var?v3?=?Add(v1,?v2);var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("TestTestClass 3對象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("TestTestClass?v3-v2?"?+?((long)h3.AddrOfPinnedObject()?-?(long)h2.AddrOfPinnedObject()));
}
static TestClass Add(TestClass v1, TestClass v2)
{var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("Add中的v1對象地址= " + (long)h1.AddrOfPinnedObject());var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("Add中的v2對象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("Add中?v2-v1?"?+?((long)h2.AddrOfPinnedObject()?-?(long)h1.AddrOfPinnedObject()));var?v3?=?new?TestClass();v3.i = v1.i + v2.i;var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("Add中的v3對象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("Add中 v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));Console.WriteLine("Add中 v3-v1 " + ((long)h3.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));return v3;
}

30e386e20410b49b5fbc61ae524e2167.png

自定義class,每次都是新地址,沒有留用性,并且地址都是在增加。

當然引用類型的地址不是一成不變的,因為有垃圾回放,重新整理的過程,本例用用Pinned的方式固定,不過代碼量少的情況也不一定能觸發回收。

a09ef79218aa1d4e64c15ab5bad0c0e7.jpeg

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284571.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284571.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284571.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ArcGIS實驗教程——實驗四十五:坐標直接轉點、線、面案例教程(Create Features From Text File)

外業實測坐標(X、Y、Z)在ArcGIS中可以方便的生成點、點自動連成線、線轉面。本實驗講解ArcGIS中從數據文件創建要素工具(Create Features From Text File)生成點、線、面。 文章目錄 一、工具快速入門1. 工具添加2. 工具用法二、工具使用方法1. 創建點(Point)2. 創建多點…

湖北省軟件行業協會會員單位全名錄(2014年的信息)

理事長單位 1.烽火通信科技股份有限公司 副理事長單位&#xff08;排名不分先后&#xff09; 2.武漢大學計算機學院 3.武漢天喻信息產業股份有限公司 4.武漢開目信息技術有限責任公司 5.武漢鋼鐵工程技術集團自動化有限責任公司 6.武漢菲旺軟件技術有限責任公司 7.立得空間信息…

靜態html引入js添加隨機數后綴防止緩存

在web項目開發中&#xff0c;頁面引入js被修改時&#xff0c;為避免瀏覽器緩存引起的問題&#xff0c;在引入js時&#xff0c;給js名后面加上隨機數&#xff0c;以保證每次都發送新的請求。 在jsp中&#xff0c;一般通過后臺取隨機數即可&#xff0c;代碼如下: <script src&…

[轉]SDK與API區別

轉載&#xff1a;https://www.zhihu.com/question/21691705/answer/149935191 SDK&#xff08;software development kit&#xff09;&#xff0c;中文可譯為“軟件開發工具包”。 一般都是一些被軟件工程師用于為特定的軟件包、軟件架構、硬件平臺、操作系統等建立應用軟件的開…

詳談如何定制自己的博客園皮膚【轉】

轉自&#xff1a;http://www.cnblogs.com/jingmoxukong/p/7826982.html 目錄 前言Quickstart定制博客園 CSS 的原理頁面定制CSS代碼博客側邊欄公告頁首Html代碼頁腳Html代碼定制細節獨立控件小老鼠游戲動畫動畫時鐘百度分享欄Github 角標簽云背景動畫動態標題文章內容樣式定制帶…

【ArcGIS微課1000例】0025:ArcGIS Online當前未連接到在線資源終極解決辦法

ArcGIS Online在線資源列表: World Imagery: 底圖服務: 中國地圖彩色版: 打開ArcGIS時,系統托盤提示“ArcGIS Online當前未連接到在線資源”,如下圖所示,如果無法連接到ArcGIS Online,則就無法添加在線資源,如World Imagery等。 關于該問題,網上有多種解決辦法,然而…

學習.NET ,提升.NET技能,這些公眾號得關注

時逢七月&#xff0c;白云在天&#xff0c;綠水環山&#xff0c;甚是悠閑。可是&#xff0c;學路上的我們卻四處彷徨。學海無涯&#xff0c;我仍苦尋渡口&#xff1b;學路漫漫&#xff0c;我卻愁無舟楫。我不禁四下掃視&#xff0c;自語問天&#xff0c;學須有成&#xff0c;可…

Excel表格從指定部分重新分頁打印的兩種方法

Excel表格現在已經成為了一個極其重要的辦公工具&#xff0c;尤其是在數據處理方面&#xff0c;它可以進行各種數據的處理、統計分析和輔助決策操作&#xff0c;但是在日常工作中我們經常對一些功能無從下手&#xff0c;例如在進行表格內容打印時&#xff0c;需要將其中內容從某…

華為筆記本Win11更新時由于驅動問題引起藍牙鼠標經常斷開問題解決方法

每次Win11升級后如果發現藍牙鼠標經常斷開&#xff0c;打開“華為電腦管家”執行以下操作即可&#xff1a; 然后更新藍牙驅動 重啟電腦OK。

【ArcGIS微課1000例】0026:ArcGIS10如何自定義工具條?

ArcGIS中,可以創建用戶工具條,將自己常用的工具命令放到一起,方便實用提高工作效率。本文講解如何自定義工具條并添加與刪除工具。 1. 創建工具條 點擊【自定義】菜單→【自定義模式】,如下圖所示: 或者在任一工具條上點擊最后面的下拉三角形→自定義: 以上兩種方法都可…

基于 KubeSphere 流水線的 GitOps 最佳實踐

背景Kubesphere 3.3.0 集成了 ArgoCD&#xff0c;但與筆者目前使用的 K8S 版本不兼容。再者&#xff0c;目前 Kubesphere 中持續集成和流水線打通還是不太友好&#xff0c;也缺少文檔說明&#xff08;可能是筆者沒有找到&#xff09;。目前遇到最主要的問題就是流水線制作完成的…

【ArcGIS微課1000例】0027:ArcGIS屬性表(dbf)轉Excel的4中方法

ArcGIS中的矢量數據Shapefile屬性表存放在后綴名為.dbf的文件中,它是GIS數據分析的核心,如果將屬性表轉為別的平臺使用,一般需要將其轉為Excel格式。本文以ArcGIS自帶矢量數據continent為例,講解dbf轉excel的常見4種方法。 文章目錄 1. 表轉Excel工具2. 直接打開3. 導出屬性…

微信公眾號自定義菜單直接跳轉到小程序指定頁面

首頁我們要先拿到需要的小程序的頁面地址&#xff1a;&#xff08;如何拿到小程序頁面地址自行百度&#xff09; 然后登錄公眾號后臺&#xff0c;添加自定義菜單&#xff1a; 菜單的路徑選擇“跳轉小程序” &#xff0c;從綁定的小程序中選擇要跳轉的小程序&#xff0c;默認小程…

javascript高級程序設計 學習筆記 第五章 上

第五章引用類型的值(對象)是引用類型的一個實例。在 ECMAScript 中,引用類型是一種數據結構, 用于將數據和功能組織在一起。它也常被稱為類,但這種稱呼并不妥當。盡管 ECMAScript 從技術上講是一門面向對象的語言,但它不具備傳統的面向對象語言所支持的類和接口等基本結構。引用…

Windows Hook

啥是windows的鉤子&#xff1f;鉤子故名思議就是在嵌入到正常執行程序的功能。對于windows來說&#xff0c;每個系統和應用程序之間的交互是使用消息機制來進行。比如點擊應用程序上面的某個按鈕&#xff0c;就是發送了事件給了應用程序。windows鉤子的作用就是在事件發送給應用…

HTTP協議之Expect爬坑

前言今天&#xff0c;在對接一個第三方平臺開放接口時遇到一個很棘手的問題&#xff0c;根據接口文檔組裝好報文&#xff0c;使用HttpClient發起POST請求時一直超時&#xff0c;對方服務器一直不給任何響應。發起請求的代碼如下&#xff1a;using (var httpClient new HttpCli…

【ArcGIS微課1000例】0028:ArcGIS根據屬性快速分割生成多個shp文件

ArcGIS10.5及以上的版本提供了按屬性分割工具,(分析工具->提取->按屬性分割)工具。也可以使用10.2版本的分割工具,效果應該是一樣的。本文演示使用分割工具批量快速提取一個縣范圍內的多個鎮,生成多個鎮矢量shp數據。 擴展閱讀:【ArcGIS遇上Python】ArcGIS Python按…

Win11 恢復 Win10經典右鍵菜單 親測有效

管理員運行命令&#xff1a; reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve 重…

把一個字符串里符合表情文字標簽的地方全部替換為相應的圖片的方法

1、表情數據&#xff1a; var emotion [{"name": "Expression_1","text": "[微笑]"},{"name": "Expression_2","text": "[撇嘴]"},{"name": "Expression_3","text&…

【ArcGIS微課1000例】0029:ArcGIS繪制平行線(構造平行公路)

在實際工作中,有時需要繪制平行線,比如道路兩邊的邊界線,可以使用“平行復制”功能快速繪制平行線,本文介紹如何使用“平行復制”功能快速繪制平行線。 1. 加載公路矢量 加載配套實驗數據包中的數據0029.rar中的矢量數據:公路。 2. 生成平行公路 點擊“編輯器”→“開始…