# C++ 記憶體模式 ###### tags: `C++` ## 儲存期間 **linkage(連結性)** 指的是一個變數名稱再不同的檔案之間的共用方式,可以分為**外部連結(external linkage)** 與**內部連結(internal linkage)** 。 1. 無連結性 : 相同的scope中不能有相同的名稱 ```cpp= int c; int c = 1; //編譯會報錯 因為同名 ``` 2. 內部連結性 : 在檔案當中的static具有這種連結性, 檔案內可以有同名的名稱, 但是這些相同的的名稱最會被整合為1個 只對於當前檔案具有此特性,當到外部檔案後有相同名稱的static宣告後,會產生兩個不一樣的宣告。 ```cpp= static int x; int main() { ..... return 0; } static int x =3; ``` 3. 外部連結性 : 檔案範圍中非static宣告,不同的檔案之中可以有同名的宣告, 而且這些相同的名稱最後會被整合成1個 ```cpp= //test1.cpp extern int i = 3; ``` ```cpp= //test2.cpp extern int i; int main() { std::cout << "i : " << i << std::endl; return 0; } ``` 有關儲存資料的記憶體架構可以區分四個方法 , 每種方法會使資料儲存的時間不同: **1. 自動儲存時間(automatic storage duration)** 宣告在函數中的變數(包括函數傳入的參數),具有自動儲存期間。當**程式執行進入到函數當中會產生他們**,**離開時會釋放他們所占用的記憶體**。 ```cpp= int main() { int price = 5 ; //當程式執行到這時會產生一個price { //進到這個區塊時 , price 同樣會保留且可以調用 std::cout << "price is : " << price <<std::endl; int new_price = 2; //在區塊中產生一個new_price ,儘可以在此區塊調用 std::cout << "new price is : " << new_price << std::endl; } //區塊的結尾 離開這個區塊後則無法調用到new_price std::cout << "the final price is : " << price <<std::endl; } /** price is : 5 new price is : 2 the final price is : 5 **/ ``` ```cpp= int main() { int price = 5 ; //當程式執行到這時會產生一個price { //進到這個區塊時 , price 同樣會保留且可以調用 std::cout << "price is : " << price <<std::endl; //與區塊外的變數同名但認為是區域變數,此時會hide區塊外的變數以這個price為準 //當離開區塊後又會以原始的price為主 int price = 2; std::cout << "new price is : " << price << std::endl; } //區塊的結尾 離開這個區塊後則無法調用到new_price std::cout << "the final price is : " << price <<std::endl; } /** price is : 5 new price is : 2 the final price is : 5 **/ ``` **2. 靜態儲存期間(static storage duration)** 宣告在**函數之外或者是透過關鍵字static定義的變數**才具有靜態儲存時間。在**程式執行的所有時間,這些變數or函數都會存在記憶體當中**。 靜態變數具有三種特性 : 外部連結(跨檔案調用) , 內部連結 (同一檔案調用) , 沒有連結 (只存在區塊當中)。與自動變數不同的是,靜態變數不會因為程式關係而增加,所以編譯器會配置固定大小的記憶體位置來儲存這些變數,但要注意的是假如我們並沒有初始化靜態變數的值,編譯器會將值設定為0。 ```cpp= int a = 1; //靜態儲存期間 , 具外部連結特性 static int b = 10; //加了staitc關鍵字 ,僅有內部連結特性 int main() { } void testA(int k) { static int c = 50; //沒有連結性 ,只可以在此函數中使用 ,離開此函數時同樣存在 int d = 30; //自動變數 , 當離開此函時已釋放 } ``` >`static`關鍵字用於區域內宣告時,表示該變數沒有連結性。 >`static`關鍵字當用於區域外時,表示為內部連結性。 **3. 執行續儲存期間(thread storage duration)** 一個cpu具有好幾個執行續可以同時工作,我們可以透過執行續將程式分割成好幾個部份給執行續執行。**透過thread_local關鍵字來宣告變數,只要這條執行續還在變數也就還在。** **4. 動態儲存期間(dynamic storge duration)** 當我們需要實例化一個物件時我們會用new運算子來處理,這時會在程式執行時配置一個記憶體位置給該物件,要釋放記憶體則是需要透過delete來釋放(ex: new 一個 pointer / class)。**這個記憶體則具有動態儲存期間(堆疊heap)**。 最後統整一下各個期間的特性 | 特性 | 期間類別 | scope | 連結性 | 宣告方式 | -------- | -------- | -------- | -------- | -------- | | 自動變數 | 自動 | 區域中 | 無 | 宣告在區域中 | | 無連結性的靜態變數 | 靜態 | 區域中 | 無 | 使用static宣告在區域中 | | 外部連結性的靜態變數 | 靜態 | 檔案(.h) | 外部 |宣告在函數外(全域變數) | | 無連結性的靜態變數 | 靜態 | 檔案(.h) | 內部 |使用static宣告在函數外 | --- ## Scope & Linkage **scope(範圍)** 指的是變數/函數/指針等等的可見範圍 , 例如當我們在A函數中定義了一個變數,儘可以在這個函數中使用,其他函數無法調用到該變數。但是如果我們將變數定義在檔案當中,在宣告變數之後所有的函數皆可以使用(全域變數)。 ```cpp= #include <iostream> void testA() { int a = 3; std::cout << "a is " << a << std::endl; } void testB() { ///在這裡會找不到a變數 std::cout << "a is " << a << std::endl; } ``` ```cpp= #include <iostream> //定義在a之後的函數皆可以調用到a int a = 3; void testA() { std::cout << "a is " << a << std::endl; } void testB() { std::cout << "a is " << a << std::endl; } ``` 同時scope可以分為四種 區域 / 全域 / 類別 / 命名空間 : 1. lcoal scope ```cpp= void testA() { int a = 3; //<-local scope std::cout << "a is :" << a <<std::endl; } ``` 2. global scope ```cpp= int a = 3; //<-global scope void testA() { std::cout << "a is :" << a <<std::endl; } ``` 3. class scope ```cpp= class test { public: int a = 3; //<- class scope void testA() { std::cout << "a is :" << a <<std::endl; } }; ``` 4. namespace scope ```cpp= namespace fortest { int a = 3; //<- namespace scope class test { public: void testA() { std::cout << "a is :" << a <<std::endl; } }; } ```