//本文章為C/C++程式設計研習班課後心得摘要
矩陣
C/C++ 中陣列可以宣告為多維度,例如一維的序列(sequence)、二維的矩陣(matrix)
語法: 型別 變數名稱[數量];
例如 int grade[3];
如果要在宣告陣列的時候同時指定初始值:
語法: 型別 變數名稱[數量] = { 值1, 值2, 值3, … };
例如 int grade[3] = { 76, 94, 83 };
EX:
#include <stdio.h> void main(void) { // 預先輸入 10 位同學的分數,由於平均後會有小數位,故以浮點數儲存: float grade[10] = { 78.0f, 44.0f, 89.0f, 55.0f, 45.0f, 75.0f, 94.0f, 74.0f, 83.0f, 65.0f }; float sum = 0.0f, average = 0.0f; // 從 0 數到 9,把 grade 陣列中的數字累加到變數 sum: for (int i=0; i<10; ++i) { sum += grade[i]; } // 這裡也可以寫成 sum = sum + grade[i] // 計算 10 位同學的平均: average = sum / 10.0f; printf("Average grade: %f", average); }
指標
宣告方式: 型別 *變數名稱;
例如: float *ptr; 宣告一個指標 ptr,指向一個儲存浮點數的位址。
* 號也是指標的一個一元運算子(operator),不是指標乘法喔,* 運算子可以由指標取得資料,而 & 運算子可以由變數取得指標。
*指標變數 : 依型別取回(或設定)指標所指位址的數值。
例如 sum += *ptr; // 把 ptr 位址處的資料加到 sum
*ptr = 6.0f; // 把數值 6.0f 儲存到 ptr 位址處
&變數: 取得變數在記憶體中的位址
例如: ptr = ∑ // 把 ptr 的值設為 sum 變數的儲存位址,接著如果 *ptr = 0.0f 也會把 sum 更改為 0.0f
心得:
以上例float *ptr應拆成float *和ptr,表示為宣告位址資料型態和位址兩部分。
另外,*ptr = 6.0f此種表示方法前述宣告不可混談,*ptr是表示用運算子*取ptr地址中的值。
所以,*ptr=位址中的值,而宣告(float *)ptr=位址!!!
指標的運算
PC 上的視窗程式設計時,絕大多數時候,我們沒辦法這麼明確地知道資料所在的位址,所以還是透過 & 先取得某個起始記憶體位址,再繼續做操作。由於指標的內容其實是個數值,所以整數能做的加減運算也都可以用在指標上, 並且依據運算後的位址值存取資料,如下例說明:
寫法: *(指標變數 + 元素偏移量)
例如 float sum;
float *ptr = ∑
所以 *ptr 等於 sum,*(ptr + 0) 也同樣是變數 sum 裡的數值。
//sum必須要是矩陣,否則*(ptr+1)則讀取到sum後一個位址的值,可能會出錯!!
計算位移量(offset)在使用 * 運算子的做法,用在陣列上比較有效用:
如前例 float grade[10] = { 78.0f, 44.0f, 89.0f, ... };
float *ptr = grade; // 為甚麼不是 ptr = &grade,矩陣本身也可當作指標使用!!
那麼 *ptr 等於 grade[0] 等於 78.0f
*(ptr + 1) 等於 grade[1] 就是 44.0f
例如 *(ptr + 5) = 6.0f; // 把6.0放到 ptr 所在位址再偏移 5 個 float 的位置 ,效果等於 grade[5] = 6.0f;
*ptr +5 和 *(ptr + 5) 是不樣的, *ptr +5 的意義是從 ptr 位址取得數值以後,再加上 5,等於是 ptr[0] + 5 也就是 78.0f + 5 = 83.0f,而 *(ptr + 5) 則是從 ptr 起算偏移 5 個浮點數的位址取值,也就是 ptr[5] 是 75.0f,隨手加 ( ) 是良好的程式寫作習慣。
還有個約定成俗的習慣:float *ptr =NULL
就是以特殊指標值 0 來代表不指向任何地方的無效指標,例如 float *ptr = 0;,而 0 這個值也俗稱 NULL。
以 new 配置動態陣列
靜態陣列的元素數量必須是已知的數字,例如 float grade[10];,元素數量不可以是變數,例如float grade[a]; 就不可行。
程式執行多半都是載入之後才之到陣列大小,使用NEW來動態配置記憶體。
配置一個型別的空間:指標 = new 型別;
例如 int *p = new int; // 只配置一個整數的空間。
配置一連串相同型別的空間、配置為陣列:指標 = new 型別[數量];
例如 float *ptr = new float[1000]; // 配置 1000 個浮點數的空間
new 是在程式執行當中才配置記憶體,所以我們就必須考慮記憶體不足的狀況,絕大部分的編譯器都設計讓 new 在配置不到足量記憶體的時候變傳回 0,也就是 NULL
可以在程式中放置一個如下的檢查:
#define NULL 0 // 用 NULL 來代表 0,這行可能不必要因為許多 .h 檔案裡面都已經有了
float *ptr = new float[一個很大的數量];
if (ptr == NULL) { 顯示訊息"記憶體不足!" 並結束程式之類的動作; }
C/C++ 語言當中和 new 成對的保留字是 delete,也就是釋放 new 所配置的空間,由於 new 可以配置單一或多個元素的空間,所以 delete 也有兩種使用法:
用 delete 釋放 new 得到的單一元素空間:
例如 delete p;
用 delete [ ] 釋放 new 得到的一連串元素空間:
例如 delete [ ] ptr;
注意:
ptr 指向的是陣列,程式裡如果寫 delete ptr 會釋放 ptr 所指向的記憶體空間。在程式執行當中 delete 沒辦法知道你給它的指標是指向一個元素或多個元素的空間,它只知道釋放記憶體,而且因為上例中 ptr 指向儲存 1000 個浮點數的一大塊記憶體,所以 delete ptr 會釋放掉所有的記憶體,就這個例子而言,delete ptr 和 delete [ ] ptr 並沒有差別,但是,在日後介紹的物件導向程式設計當中,我們可以自訂每個元素被 delete 的時候要執行的清除動作(稱為解構動作 destruction),如果用 delete ptr 來釋放記憶體,就只有陣列中的第一個元素會執行這樣的清除動作,因為 delete 不知道它是個陣列!但如果你用 delete [ ] ptr 來釋放記憶體,delete 會對陣列中每一個元素執行清除動作,那才是正確的效果
#include <stdio.h> void main(void) { int *p = new int; // 只配置一個整數的空間 delete p; // 把 new 配置的空間釋放掉 int size = 1000000; // 用一個變數指定說要配置百萬個元素: float *ptr = new float[size]; // 用 new 運算子向系統要求萬個浮點數的空間 if (ptr != NULL) // 檢查有配置到記憶體才進行接下來的事情 { /* 對ptr 作任何處理... */ delete [] ptr; // 用完記得釋放 new 配置的空間 } else { printf("記憶體不足喔!"); } // 如果記憶體不足就警示!
}
二維陣列
宣告二維陣列:型別 變數名稱[維度1][維度2];
例如 int matrix[3][2]; // 表現一個 2(行)x3(列)的矩陣,matrix[row(列)][col(行)]
宣告同時指定初始值: 型別 變數名稱[維度1][維度2]; = { { 值a1,值a2}, {值1b, 值2b}, {值1b, 值2b} };
例如 int matrix[3][2] = { { 11, 10 }, { 23, 4 }, { 5, 7 } };
[y][x] 來取用二維陣列中某個元素的時候,編譯器至少必須知道 x 的元素數量,才能夠依照 (y * x維度的元素數量 + x) 來得知 [y][x] 元素的所在位址,然而如果 x 維度和 y 維度都是執行時期才能夠得知數值的變數就沒辦法了。
ex:[5][2]但是在陣列中是從0開始計算,所以是3X6。其位址為6*3+3
動態配置二維陣列的話,有兩個常見的方案如下,假設同樣是要配置 matrix[3][2] 這個二維陣列:
方案一:配置一個指標的陣列,再逐個指標配置
float **matrix = new float *[3];
for (int i=0; i<2; ++i)
{ matrix[i] = new float [2]; }
// 但是 delete 的時候就很麻煩:
for (int i=0; i<2; ++i)
{ delete [] matrix[i]; }
delete matrix;
方案二:配置一個一維陣列,再自己計算位置
float *matrix = new float[3*2]; 那麼存取 (x, y)的元素就是 matrix[2*y + x] = ...
參考型別
參考型別的變數會和另一個變數佔用同一塊記憶體空間,其實參考型別的內部原理和指標相同,都是儲存另一個變數的記憶體位址
參考型別的變數沒有類似 NULL 這類的特殊值,也不能進行 *(變數+偏移量) 之類的運算、也不能被強制轉型,參考型別的變數使用起來就像一般的資料變數,只是你更動它的數值的時候,另一個變數的數值也會跟著變(因為它們事實上是同一塊記憶體空間)。
宣告方式: 型別 &變數名稱 = 另一個變數;
例如: float &f = sum;
之後更動f 就跟更動 sum 一樣。
//輸出入參數有「不可以不填寫」或「不可以採用其他型別」的特性
重點整理:
- C / C++ 語言可以宣告一維、二維…多維陣列。
- 用 *運算子可由指標取變數的數值,用&可由變數取位址。
- 可以用 new 配置一段記憶體給指標,用 delete 釋放之。
- 指標和陣列變數是一樣的東西,指標也可以用 [ ] 取元素。
- NULL就是0,是特殊的指標值。