【從零開始入門unity游戲開發之——C#篇04】棧(Stack)和堆(Heap),值類型和引用類型,以及特殊的引用類型string

文章目錄

  • 知識回顧
  • 一、棧(Stack)和堆(Heap)
    • 1、什么是棧和堆
    • 2、為什么要分棧和堆
    • 3、棧和堆的區別
    • 4、總結
  • 二、值類型和引用類型
    • 1、那么值類型和引用類型到底有什么區別呢?
      • 值類型
      • 引用類型
    • 2、總結
  • 三、特殊的引用類型string
    • 1、為什么說string是特殊的引用類型?
    • 2、理解字符串(string)引用類型
    • 3、如何證明呢?
      • 使用 `GetHashCode` 方法
      • 通過斷點調試直接查看變量指針內存地址
    • 4、總結
  • 專欄推薦
  • 完結

知識回顧

C# 中的變量類型可以分為 值類型引用類型 兩大類。

值類型

變量類型描述范圍
byte無符號8位整數0 到 255
sbyte有符號8位整數-128 到 127
short有符號16位整數-32,768 到 32,767
ushort無符號16位整數0 到 65,535
int有符號32位整數-2,147,483,648 到 2,147,483,647
uint無符號32位整數0 到 4,294,967,295
long有符號64位整數-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
ulong無符號64位整數0 到 18,446,744,073,709,551,615
float32 位單精度浮點數±1.5 × 10^?45 到 ±3.4 × 10^38(精度約7位有效數字)
double64 位雙精度浮點數±5.0 × 10^?324 到 ±1.7 × 10^308(精度約15–16位有效數字)
decimal128 位高精度小數±1.0 × 10^?28 到 ±7.9 × 10^28(精度約28–29位有效數字)
bool8 位布爾型truefalse
char16 位單一字符型Unicode字符(0 到 65,535)

引用類型

變量類型描述范圍
string字符串任意長度的字符序列(理論上最多可達到 2GB)

一、棧(Stack)和堆(Heap)

要了解值類型和引用類型的區別,我們得先得棧和堆的概率有個了解。

1、什么是棧和堆

簡單理解就是,程序運行時,它的數據必須存儲在內存中。棧和堆就是計算機內存中的兩種不同的存儲區域。

2、為什么要分棧和堆

通過分棧和堆,程序可以在性能和內存管理上做出平衡,從而讓程序既高效又靈活。

3、棧和堆的區別

  • 棧空間比較小,但是讀取速度快。
  • 棧存儲的是一些簡單的數據
  • 棧遵循先進后出原則,棧就像一個堆疊的盤子。你每次放入一個新盤子(數據),都會把它放在最上面。拿東西的時候,也都是從最上面拿,所以非常快速。
  • 棧里的數據只在當前函數或方法運行時有效,一旦方法執行完畢,這些數據就會自動被銷毀。
    在這里插入圖片描述

  • 堆空間比較大,但是讀取速度慢。
  • 堆存儲的是一些較大的數據。
  • 堆就像一個大大的垃圾堆,可以隨意放東西。它不按照順序來存放數據,而是根據需要分配空間,可以存儲更復雜的對象
  • 堆中的數據不會像棧那樣自動清理。(但在 C# 中,垃圾回收會自動清理不再使用的對象)

在這里插入圖片描述

4、總結

實際上,我們寫程序并不需要關心內存是如何使用的,C#已經幫我們做好了。這里只是簡單介紹這個概念,有些知識看不懂也沒關系,比如垃圾回收,后面肯定還會詳細介紹。現在有個印象就行。


二、值類型和引用類型

在 C# 中,數據類型分為兩大類:值類型(Value Types)和 引用類型(Reference Types)。

我們目前學了值類型和引用類型只有變量,但是其實不止

  • 值類型其實還有結構體(Struct)枚舉(Enum)
  • 引用類型還有類(Class)數組(Array)委托(Delegate)

這些我們后面會一一介紹,現在了解一下就行。

1、那么值類型和引用類型到底有什么區別呢?

因為只學了變量,這里就用變量舉例。

值類型

  • 直接存儲數據,值類型的變量直接保存它們的數據。值類型直接存儲在上。
  • 值類型賦值時,會復制值本身

比如

int a, b;
a = 100;
b = a;
Console.WriteLine("a的值:" + a);
Console.WriteLine("b的值:" + b);a = 20; // 重新給a賦值,b的值不會改變
Console.WriteLine("a的值:" + a);
Console.WriteLine("b的值:" + b);

打印結果,正如前面所說,重新給a賦值,b的值不會改變
在這里插入圖片描述

畫圖說明
在這里插入圖片描述
解釋

  • 聲明變量 int a, b;
    當你聲明兩個整數變量 a 和 b 時,編譯器會在棧上為它們各自分配32位的存儲空間。此時,這兩個存儲空間是空的,沒有初始化任何值。

  • 賦值 a = 100;
    當你給變量 a 賦值為 20 時,棧上的存儲空間 a 會被寫入值 20。這個操作不會重新分配內存,而是直接在已經分配的內存位置寫入新的值。

  • 賦值 b = a;
    當你執行 b = a; 時,棧上的存儲空間 b 會被寫入 a 的當前值。此時,a 和 b 都存儲了值 20,但它們是獨立的存儲空間。

  • 重新賦值 a = 20;
    當你再次給 a 賦值為 20 時,棧上的存儲空間 a 會被更新為新的值 20。這不會重新分配內存,而是直接在已經分配的內存位置寫入新的值。

引用類型

  • 間接存儲數據,引用類型的變量保存的是對實際數據所在位置的引用或地址(也叫指針),而不是數據本身。引用類型存儲在棧上的引用(或指針)和堆上的實際數據
  • 引用類型賦值時,會復制引用,但實際的數據不會復制。

畫圖說明,假設a b c都是引用類型
在這里插入圖片描述
解釋

  • 聲明引用類型 a 和 b;

    這時 a 和 b 都是空引用,它們在棧上分配了空間,但它們指向的堆內存地址尚未確定,二者目前都沒有引用任何實際的對象。

  • 給 a 賦值:

    此時,a 作為棧上的一個引用變量,指向堆上的值。b 仍然是空引用。

  • b = a; b 也指向 a 的堆值:

    此時,a 和 b 都存儲相同的堆內存地址,指向相同的堆對象。

  • a 修改為新值:

    這時候a b的值就都變成了新值

  • 重新定義 c,并給 c 賦值

    此時,c 是一個新的引用類型變量,它在棧上存儲了指向堆上c值的地址,且與 a 和 b 的值互不影響。

ps:有些小伙伴可能會說了,前面不是說了string不就是引用類型嗎,為什么不用string舉例呢,這樣不是更加直觀?其實是因為string是特殊的引用類型,這個問題我接下來會說。

2、總結

特性值類型 (Value Type)引用類型 (Reference Type)
存儲方式存儲數據的值本身存儲數據的引用(內存地址)
賦值行為賦值時會復制數據,原始值和復制值互不影響賦值時會復制引用,兩個變量指向同一個對象
存儲位置通常存儲在棧上 (stack),但結構體和其他類型可能存儲在堆上存儲在堆上 (heap),引用存儲在棧上
初始化默認值為零或空值(如 0falsenull默認值為 null
內存管理系統負責管理內存(棧上分配的內存自動釋放)由垃圾回收器 (GC) 管理內存
常見類型基本數據類型(如 intfloat 等)、結構體、枚舉類、數組、委托、字符串等

三、特殊的引用類型string

1、為什么說string是特殊的引用類型?

學了前面引用類型的知識,我們可以拿string測試一下試試。

string str1, str2;
str1 = "名";
str2 = str1;
Console.WriteLine("str1的值:" + str1);
Console.WriteLine("str2的值:" + str2);str1 = "字";//重新賦值str1
Console.WriteLine("str1的值:" + str1);
Console.WriteLine("str2的值:" + str2);

按前面引用類型的概念,可能你想說第二次打印的結果應該是"字" "字"

實際上真是這樣嗎?我先來看看執行結果
在這里插入圖片描述
有人會說,如果是值類型,結果倒還說的過去.但是不是說string是引用類型么?如果是引用類型的話。輸出的結果難道不應該是: "名""名""字" "字"么?

2、理解字符串(string)引用類型

理解字符串(string)在C#中的行為確實可能有些困惑,因為它們在某種程度上表現出值類型和引用類型的特性。讓我們來詳細解釋一下。

  • 字符串是不可變
    字符串在C#中是不可變的,這意味著一旦你創建了一個字符串對象,就不能修改它的內容。當你嘗試修改一個字符串時,實際上是創建了一個新的字符串對象。

  • 字符串為什么是引用類型
    因為它們在堆上分配內存,并且在棧上存儲對堆上對象的引用。因此,多個變量可以引用同一個字符串對象。

3、如何證明呢?

使用 GetHashCode 方法

雖然這并不返回內存地址,但 GetHashCode 方法會返回一個與字符串內容相關的哈希值。這個值可以作為字符串的“標識符”,有時候在調試中,它能幫助你判斷是否為同一個字符串實例。

string str1 = "xxxx";
string str2 = str1;
Console.WriteLine(str1.GetHashCode());
Console.WriteLine(str2.GetHashCode());str1 = "yyyy";
Console.WriteLine(str1.GetHashCode());
Console.WriteLine(str2.GetHashCode());

結果
在這里插入圖片描述

通過斷點調試直接查看變量指針內存地址

在這里插入圖片描述
值類型,一開始內存地址就不一樣
在這里插入圖片描述
string引用類型,開始地址一樣,重新賦值后地址不一樣了
在這里插入圖片描述
在這里插入圖片描述

4、總結

字符串不叫值類型,因為它們確實具有引用類型的基本特性:在堆上分配內存,并且在棧上存儲引用。盡管字符串的不可變性使得它們在某些方面表現得像值類型,但從技術上講,它們仍然是引用類型。

由于字符串的不可變性,即使它們是引用類型,修改一個字符串變量不會影響其他引用相同字符串的變量。這是因為當你修改字符串時,實際上是創建了一個新的字符串對象,并將變量的引用指向了這個新對象。

string雖然方便,但是有一個小缺點就是頻繁的改變string重新賦值會產生內存垃圾,優化替代方案我們會在后面進行講解


專欄推薦

地址
【從零開始入門unity游戲開發之——C#篇】
【從零開始入門unity游戲開發之——unity篇】
【制作100個Unity游戲】
【推薦100個unity插件】
【實現100個unity特效】
【unity框架開發】

完結

贈人玫瑰,手有余香!如果文章內容對你有所幫助,請不要吝嗇你的點贊評論和關注,你的每一次支持都是我不斷創作的最大動力。當然如果你發現了文章中存在錯誤或者有更好的解決方法,也歡迎評論私信告訴我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奮斗的開發者,閑暇之余,邊學習邊記錄分享,站在巨人的肩膀上,通過學習前輩們的經驗總是會給我很多幫助和啟發!如果你遇到任何問題,也歡迎你評論私信或者加群找我, 雖然有些問題我也不一定會,但是我會查閱各方資料,爭取給出最好的建議,希望可以幫助更多想學編程的人,共勉~
在這里插入圖片描述

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

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

相關文章

【C語言實現:用隊列模擬棧與用棧模擬隊列(LeetCode 225 232)】

LeetCode刷題記錄 🌐 我的博客主頁:iiiiiankor🎯 如果你覺得我的內容對你有幫助,不妨點個贊👍、留個評論?,或者收藏?,讓我們一起進步!📝 專欄系列:LeetCode…

【Python】Selenium 爬蟲的使用技巧和案例

引言 Selenium 是 Python 中功能強大的自動化測試工具,因其能夠操控瀏覽器進行模擬操作,被廣泛應用于網頁數據爬取。相比傳統的 requests 等庫,Selenium 能更好地應對動態加載內容和復雜交互場景。本文將詳細介紹 Selenium 爬蟲的使用技巧,并提供實際案例來幫助讀者快速上…

MySQL SQL語句性能優化

MySQL SQL語句性能優化指南 一、查詢設計優化1. 避免 SELECT *2. 使用 WHERE 進行條件過濾3. 避免在索引列上使用函數和表達式4. 使用 LIMIT 限制返回行數5. 避免使用子查詢6. 優化 JOIN 操作7. 避免全表掃描 二、索引優化1. 使用合適的索引2. 覆蓋索引3. 索引選擇性4. 多列索引…

Mybatis動態sql執行過程

動態SQL的執行原理主要涉及到在運行時根據條件動態地生成SQL語句,然后將其發送給數據庫執行。以下是動態SQL執行原理的詳細解釋: 一、接收參數 動態SQL首先會根據用戶的輸入或系統的條件接收參數。這些參數可以是查詢條件、更新數據等,它們…

java jar包加密 jar-protect

介紹 java 本身是開放性極強的語言,代碼也容易被反編譯,沒有語言層面的一些常規保護機制,jar包很容易被反編譯和破解。 受classfinal(已停止維護)設計啟發,針對springboot日常項目開發,重新編寫安全可靠的jar包加殼加密技術,用于保護軟件版權。 使用說…

Linux:Git

Git常見指令: git help xx_command git xx_command --help git --version 查看git版本git config --global user.name "xxx_name" 全局級別的簽名設置,全局的放在本用 git config --global user.ema…

【WiFi】WiFi中RSSI、SNR、NF之間關系及說明

RSSI(接收信號強度指示) 定義: RSSI 是一個相對值,用于表示接收到的無線信號的強度。它通常由無線設備的硬件(如無線網卡或無線芯片)直接提供。 計算: RSSI 的計算通常是由設備的無線芯片完成的…

提升音頻轉錄準確性:VAD技術的應用與挑戰

引言 在音頻轉錄技術飛速發展的今天,我們面臨著一個普遍問題:在嘈雜環境中,轉錄系統常常將非人聲誤識別為人聲,導致轉錄結果出現錯誤。例如,在whisper模式下,系統可能會錯誤地轉錄出“謝謝大家”。本文將探…

[ZMQ] -- ZMQ通信Protobuf數據結構 1

1、前言背景 工作需要域間實現zmq通信,剛開始需要比較簡單的數據結構,比如兩個bool,后面可能就需要傳輸比較大的數據,所以記錄下實現流程,至于為啥選擇proto數據結構去做大數據傳輸,可能是地平線也用這個&…

順序表的使用,對數據的增刪改查

主函數: 3.c #include "3.h"//頭文件調用 SqlListptr sql_cerate()//創建順序表函數 {SqlListptr ptr(SqlListptr)malloc(sizeof(SqlList));//在堆區申請連續的空間if(NULLptr){printf("創建失敗\n");return NULL;//如果沒有申請成功&#xff…

React和Vue中暴露子組件的屬性和方法給父組件用,并且控制子組件暴露的顆粒度的做法

React 在 React 中,forwardRef 是一種高級技術,它允許你將 ref 從父組件傳遞到子組件,從而直接訪問子組件的 DOM 節點或公開的方法。這對于需要操作子組件內部狀態或 DOM 的場景非常有用。為了使子組件能夠暴露其屬性和方法給父組件&#xf…

《C++ 實時視頻流物體跟蹤與行為分析全解析》

在當今科技飛速發展的時代,視頻監控與智能分析技術在眾多領域發揮著極為重要的作用。從安防監控到智能交通,從工業自動化到人機交互,利用 C 處理實時視頻流中的物體跟蹤和行為分析成為了熱門且極具挑戰性的研究與開發方向。本文將深入探討其中…

5G中的隨機接入過程可以不用收RAR?

有朋友提到了一種不用接收RAR的RA過程,問這個是怎么回事。其實在剛剛寫過的LTM cell switch篇章中就有提到,這里把所有相關的內容整理如下。 在RACH-less LTM場景,在進行LTM cell switch之前就要先知道target cell的TA信息,進而才…

git 導出某段時間修改的文件 windows

第一步:列出兩次commitID之間的文件變動 git diff oldid newid --name-only// 例如 git diff 4a886c57a8b5611a2abcfcd120461c2e92f7029a HEAD --name-only 4a886c57a8b5611a2abcfcd120461c2e92f7029a 代表之前 HEAD 代表最新或者換成某次commitID 例如&#xf…

Qt 聯合Halcon配置

文章目錄 配置代碼窗口綁定 配置 選擇添加庫 選擇外部庫 LIBS -LC:/Program Files/MVTec/HALCON-17.12-Progress/lib/x64-win64/ LIBS -lhalconcpp\-lhdevenginecpp\-lhalconINCLUDEPATH C:/Program Files/MVTec/HALCON-17.12-Progress/include DEPENDPATH C:/Program Fil…

new URL(`../assets/images/${name}`, import.meta.url).href

背景: 文章講述了Vite框架中關于資源文件(如圖片)在默認配置下,如何正確處理開發環境和打包后的不同引用方式。重點介紹了使用import.meta.url和new URL() 來動態獲取并處理靜態資源URL的方法,以及注意事項&#xff0…

8、筆記本品牌分類介紹:LG - 計算機硬件品牌系列文章

LG筆記本品牌以其高性能和先進技術而聞名,?提供多種型號以滿足不同用戶的需求。? LG筆記本產品線包括多種類型,?以滿足不同用戶的需求。?其中,?LG Gram Pro系列以其超薄設計和高性能配置受到關注。?該系列筆記本采用16:10的OLED顯示屏&…

367_C++_計算mouse移動過程中,視頻框的右側、底部邊距,以及根據實時的右側、底部邊距計算—視頻框的左上角位置

代碼分析 1. restorePos 方法 restorePos 的作用是恢復 NavigationFrame 的位置,將其移動到父窗口或者指定矩形內的特定位置。 void NavigationFrame::restorePos() {// 獲取目標矩形:優先使用 `m_pRect`,否則默認使用視頻區域或父窗口區域RSRect videoRect(m_pVide

Tiptap,: 富文本編輯器入門與案例分析

Tiptap 是一個現代的富文本編輯器,基于 ProseMirror 打造,旨在提供一個靈活且功能強大的文本編輯解決方案。它具有開箱即用的能力,同時也允許開發者根據業務需求進行高度定制化擴展。與傳統的富文本編輯器相比,Tiptap 提供了更精細…

scala的泛型類

泛型:類型參數化 泛型類指的是把泛型定義到類的聲明上, 即:該類中的成員的參數類型是由泛型來決定的. 在創建對象時, 明確具體的數據類型. 定義格式: class 類名(成員名:數據類型) class 類名[泛型名](成員名:泛型名) 參考代…