# 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;
}
};
}
```