〈 C++學習日記 #2〉再談指標 Pointer (Part.2)


1. 雙重指標

雙重指標顧名思義就是存放另一個指標在記憶體中的位址。可以透過以下語法宣告:

int **dptr; // or int *(*dptr)

//將dptr指向另一個指標
int a = 10, *ptr;
ptr = &a;
dptr = &ptr;

2. 二維陣列 & 雙重指標

在前篇基本指標 Pointer (Part.1)中,我有提到一為陣列的名稱是一個指向陣列位址的指標常數(Pointer Constant),而二維陣列的概念可視為其的延伸。

假設我們宣告一個二維陣列

int a[3][4];

可以將a視為是三個一維陣列的組合,而指向這三個一維陣列位址的指標常數分別是a[0]、a[1]和a[2]a則是一個雙重指標常數,它指向由a[0]、a[1]和a[2]組成的指標常數陣列的位址。

用指標存取二維陣列可以靠指標的運算達成:

int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

for(int m=0;m<3;m++){
    for(int n=0;n<4;n++){
        cout << *(*(a+m)+n) << ' ';
    }
}

可以看到第5行我們直接使用a這個指標常數進行運算,而我們可以得到以下結論:

*(*(a+m)+n); //等於用指標表示a[m][n]

此外,函數的輸入不接受雙重指標,故如果想傳遞多維陣列到函式中,則需要使用

void Func(int [3][4]); //宣告

void Func(int a[3][4]){} //函式內容

3. 動態記憶體配置

C++基本資料型態(如int,float,char...)都是在編譯時完成記憶體的配置,他們會占用固定的記憶體空間,這種記憶體配置方式稱為「靜態記憶體配置(static memory allocation)」。

相對的,在執行期間能夠彈性調派記憶體的方式稱為「動態記憶體配置(dynamic memory allocation)」,使用動態記憶體配置時,OS會在程式執行時額外尋找一塊記憶體空間供程式使用,這個未用空間(free space)稱為記憶堆(heap)。

4. 動態記憶體配置實作

動態記憶體配置的實作,不外乎的和指標有關。基本資料的動態配置基本上就是由該資料型態的指標指向記憶堆,搭配newdelete運算子

int *ptr;
ptr = new int;
*ptr = 10; //使用ptr

當使用new運算子配置記憶體時,會導致記憶體一直被占用,直到電腦關閉為止,因此,必須使用delete運算子來釋放記憶體空間,並將ptr指向的值設為NULL以保證指標使用的安全性

//ptr使用完後
delete ptr;
ptr = NULL;

接著就要進入重點,C++中最常使用動態記憶體配置的資料結構就屬陣列,陣列常常會造成記憶體空間的浪費,因此使用動態配置就能達到最有效的使用。

int *ptr;
ptr = new int[5]{ 1, 2, 3, 4, 5 };

for(int i=0;i<5;i++){
    cout<<ptr[i]<<endl;
}

delete[] ptr;
ptr = NULL;

輸出結果

1
2
3
4
5

p.s. 字串的動態配置,std::string有提供內置的動態記憶體配置,在程式運行超出它的scope時就會自動釋放。

5. 指標、參照和函式

首先,先介紹參照(reference)。參照簡單來說就是一個變數的別名,變數和它的參照除了名子以外,記憶體位置和值都是相同的,所以基本上就是一樣的東西。與指標不同,參照在被宣告時就必須被初始化,而且設定後便無法改變參考的對象。

int a=10;
int &ref = a;

既然連記憶體位址都和原本的變數一模一樣,那為什麼需要參照呢?參照的主要功能在於函數的引數傳遞,因為是別名,所以可以避免複製大量的變數到函數去。

引數在函數間的傳遞方式有三種,第一種是最簡單的傳值(pass by value),第二種是前面有提到的透過位址當作引數傳遞,第三種就是就是此段要介紹的用參照作為引數傳遞。

void refFunc(int &); 

int main(void){
    int a =10;
    refFunc(a);
    cout << "In main function: " << a << endl;

    system("pause");
    return 0;
}

void refFunc(int &a){
    a += 1;
    cout << "In refFunc(): " << a << endl;
}

輸出結果

In refFunc(): 11
In main function: 11

傳遞參照(Pass-by-reference)相對於傳值(Pass-by-value)是更有效率的方法,因為函數在使用引數時不需要將引數物件再進行複製。而且使用參照能夠在該函數內對變數的值做更動,相對於傳值來說在某些狀況下更加便捷。而用指標傳遞則是一個相對差的做法。這篇文章中有對於參照和指標,以及在何種情況應用哪種方法的詳細介紹,有興趣可以去看看。

6. 結語

我還是一無所知

#algorithm #data structure #C++







你可能感興趣的文章

MTR04_0622

MTR04_0622

C# ShowDiglog關閉視窗後事件動作

C# ShowDiglog關閉視窗後事件動作

Java Stream intermediate operations are lazily executed

Java Stream intermediate operations are lazily executed






留言討論