//本文章為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 = &sum;    // 把 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 = &sum; 
所以 *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,是特殊的指標值。

 

 

 

 

 

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 winage 的頭像
    winage

    winage的部落格

    winage 發表在 痞客邦 留言(0) 人氣()