Download presentation
Presentation is loading. Please wait.
1
第五章 特殊型態的指標
2
本章學習目標: 認識特殊的資料類型--指標,如何宣告一個指標變數並進行初始化動作。 利用new運算子將指標直接初始化。
在.NET Framework下,又提供那些類型的指標。 討論指標和陣列二者之間如何相輔相成的搭配使用。 介紹動態指標的應用:動態陣列和指標的指標。
3
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-3-2 字串指標陣列 5-2 指標和陣列 5-3 字串和指標 5-3-1 使用字串指標 5-2-2 二維陣列指標 5-4 指標應用 5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-1-4 以new初始化指標 5-1-3 指標初始化 5-1-2 宣告指標 5-1-1 認識記憶體位址 5-2-1 一維陣列指標 5-4-3 指標的指標 5-4-2 CLR與動態陣列 5-4-1 建立動態陣列
4
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-3-2 字串指標陣列 5-2 指標和陣列 5-3 字串和指標 5-3-1 使用字串指標 5-2-2 二維陣列指標 5-4 指標應用 5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-1-4 以new初始化指標 5-1-3 指標初始化 5-1-2 宣告指標 5-1-1 認識記憶體位址 5-2-1 一維陣列指標 5-4-3 指標的指標 5-4-2 CLR與動態陣列 5-4-1 建立動態陣列 5-1 指標的宣告和使用
5
5-1 指標的宣告和使用 「指標」(pointer)可以說是C語言裡一種特殊的資料型態。
透過指標,我們可以建立串列、佇列、堆疊與二元樹等資料結構 甚至可利用指標來進行動態的記憶體配置,或者取得記憶體中的資料內容,是C++中一個相當重要的特性。 本節將為大家介紹「指標」的概念,並進一步討論在C++程式中宣告與使用指標的方式。
6
章節目錄 5-1-1 認識記憶體位址 5-1 指標的宣告和使用
7
5-1-1 認識記憶體位址 在C++程式中,「指標」是一個指向某個記憶體位址的特殊變數。 它和一般變數有何差異?
在前面章節所介紹的變數,是儲存於記憶體的資料 而指標所儲存則是用來存放該資料的記憶體位址。 這聽起來很像繞口令!當我們宣告一般變數時,記憶體會畫出一塊空間來存放變數的資料。 記憶體的每一塊空間都會有自己的編號,我們稱為『位址』(Address)。
8
記憶體位址圖 假設我們宣告一個變數是「int value = 20」 表示某個記憶體位址會儲存value值為20的資料,如下圖5-1:
每個變數儲存的記憶體區塊會以位址表示
9
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1 指標的宣告和使用
10
5-1-2 宣告指標 宣告指標變數和宣告一般的變數並無不同,只是要在宣告的變數名稱上,加上「*」取值運算子(Dereference operator) 語法如下:
11
宣告指標的語法. 在上述宣告指標的語法中,在指標變數名稱前面加上「*」取值運算子,表示它是一個指標變數。
而在宣告同時,若直接指定一個數值給指標,則指標指向的是記憶體位址。 宣告指標的敘述如下:
12
宣告指標的語法.. 從上面的例子可看出,宣告指標所使用的資料型別和變數宣告時的基本資料型別都相同,如int、float、double...等。 以第4行的指標宣告來說,所代表的意義卻有些不同 由於所宣告的是一個「指向浮點數(float)」資料的指標,每一筆浮點數資料佔用的記憶體大小是4 Bytes 因此每次指標往下移動一格時,便是指向4個bytes後的記憶體位址
13
範例:memaddr.cpp
14
執行程式:memaddr.cpp 利用&取址運算子來取得陣列元素儲存的記憶體位址。 第9行宣告一個浮點數型別的value陣列並設定起始值。
第11~15行以for迴圈來讀取陣列中的元素,並利用取址運算子&來取得儲存陣列元素的記憶體位址。
15
記憶體位址和變數值的關係
16
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用
17
5-1-3 指標初始化 宣告指標變數後,第一件事便是將指標變數進行初始化的動作。
未經初始化的指標,有可能使用不當而覆寫到任意的記憶體區塊,產生無法預估的錯誤! 如果存取的對象是指標變數的話,就得透過「&」取址運算子。語法如下: 除了使用&取址運算子來輸出變數的儲存住址。我們想要取得記憶體中儲存的資料,只要利用變數名稱就能直接存取。但是要取得指標變數所指向的記憶體內的資料,必須間接使用「*」(取值運算子)
18
取值運算子的語法 在上述語法中,我們應該把指標變數看成是一個指向某種資料型別的指標。
在範例「memaddr.cpp」中,我們可以使用「&」運算子來取得變數在記憶體中的位址,便可以設定指標指向該變數,將指標予以初始化。
19
取值運算子的敘述 當然,上述的宣告過程中,第2行是大家熟悉的變數宣告
那麼第4行是什麼作用?實際上就是先宣告了一個指向整數型別的指標變數ptr,只不過它儲存的是記憶體空間的位址。 如果宣告了指標變數後,在輸出過程中並沒有加上*取值運算子,結果會如何?
20
範例:setpointer.cpp
21
執行程式:setpointer.cpp 此範例主要目的是讓大家指標變數中*取值運算子和&取址運算子的用法。
第9行宣告了一個整數變數,第11行宣告了一個整數型別的指標變數,並在宣告的同時把整數變數的位址設為指標變數的起始值。 第13行輸出指標ptr所指向的位址。這說明經過宣告的指標變數,若未加上*取值運算子產生的結果。 第15行的指標變數因為加上*取值運算子,所以間接取得所指向位址的變數值。 第17行則是將變數value的位址,利用&取址運算子來輸出。 第19行就是很單純的輸出變數value的值。
22
取值(*)和取址(&)運算子 透過執行結果,我們可以明顯看出value的位址和指標ptr中儲存的位址是相同。因此,*ptr間接取得的值和變數value所儲存的值是相同的。這樣的關係,我們以下圖來說明: 指標ptr和變數value的關係示意圖
23
使用指標時
24
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
25
5-1-4 以new初始化指標 我們已討論了指標初始化時,必須先指定某一個含有資料值的變數,然後才能把指標指向此變數的位址,基本上是一種間接初始化指標的方式。 事實上,C++語言的「new」運算子,可讓我們在程式中用來配置所需的記憶體。 利用此運算子直接初始化指標,並設定該指標所指位址上的資料值 利用new運算子直接初始指標的語法如下:
26
以new初始化指標的敘述 在宣告指標的語法中,new運算子會配置一塊大小與指定資料型態相同的記憶體空間(如浮點數即為4bytes),並將初始值存入此記憶體中,讓指標所指向位址中的資料便能直接存取。 以下是以new運算子來宣告與初始化指標的敘述如下:
27
以delete運算子釋放記憶體 由於我們使用new運算子來配置記憶體供程式使用,因此在程式結束後必需釋放所佔用的記憶體,釋放記憶體所使用的運算子為「delete」 語法如下: 以上語法的「變數名稱」為指向配置記憶體的變數名稱,它可能是變數、指標、陣列。 使用delete運算子來釋放記憶體的示範如下:
28
範例:newopr.cpp
29
執行程式:newopr.cpp 使用new運算子來初始化指標,讓指標取得記憶體的配置空間。
在第12、16行分別輸出iptr、*iptr和fptr、*fptr時,分別是記憶體位址和指標初始化時所指向的儲存值。
30
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-5 指標的移動 5-1-6 一般型別系統中的指標 5-1-4 以new初始化指標
31
5-1-5 指標的移動 「指標的移動」可視為指標在記憶體內指向位址的改變 指標移動的方式敘述如下:
而指標一次移動的單位,是以目前指標宣告資料型別所佔用的位元組(Bytes)來計算! 如此一來,指標移動時,便會依據所指向的資料型態來決定需移動多少記憶體長度到正確的位址上。 指標的移動是利用「+」與「-」運算子,同樣也可使用「++」與「--」運算子 指標移動的方式敘述如下:
32
移動指標時 若指標ptr所指向的資料型態為整數時,那麼每移動一個位址的記憶體長度便是4 Bytes,指標移動如下面圖例所: 移動指標示意圖
33
控制指標的移動 藉由控制指標的移動,便可取出對應記憶體中的資料值 控制 指標移動
陣列元素的值 範例「moveptr.cpp」,藉由控制指標移動來取出陣列元素的值,並顯示執行結果
34
範例:moveptr.cpp(1)
35
範例:moveptr.cpp(2)
36
執行程式:moveptr.cpp
37
程式解說:moveptr.cpp 程式碼第7行先宣告整數型別的一維陣列,並設定起始值,再將此陣列指派給第8行的ptr指標。
第14行先列印指標所指向的位址及儲存值。 第15行利用「+」運算子將指標向後移動3個位址,總共移動了12個Bytes,所以它會指向「0012FF64」位址,儲存值為「40」。 第19行則是利用「-」運算子將指標向前移動2個位址,總共移動了8個Bytes,所以它會指向「0012FF5C」位址,儲存值為「20」。 第23行則是利用「++」運算子將指標向後移動1個位址,移動了4個Bytes,所以它會指向「0012FF60」位址,儲存值為「30」。
38
移動的指標讀取陣列的示意圖 移動指標讀取的陣列元素
39
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-5 指標的移動 5-1-6 一般型別系統中的指標 5-1-4 以new初始化指標
40
5-1-6 一般型別系統中的指標
41
列管指標 「列管指標」是.NET Framework架構下的新型態指標
它透過記憶體的堆積(Heap)來管理,亦稱為列管物件的控制代碼(Handle)。 通常我們說共通語言執行環境(CLR)具有記憶體自動回收的機制,所指就是列管指標。 一般來說,列管指標以傳址方式來傳遞引數。 對於Visual C 來說,列管指標表示方法為「^」,將在後面的章節介紹。
42
未列管指標和未列管函式指標 「未列管指標」指的就是傳統的C++指標。 「未列管函式指標」
由於C++程式語言本身的預設值就是未列管程式碼,換句話說並未涵蓋在通用語言規範(CLS)中,這也是我們介紹的指標概念之一。 「未列管函式指標」 以傳統C++指標來提供函式位址,透過CLS的委派方式來改良原有的函式指標。
43
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-5 指標的移動 5-1-4 以new初始化指標 5-1-6 一般型別系統中的指標 5-2 指標和陣列
44
5-2 指標和陣列 指標與陣列兩者其實是互為表裡、相輔相成的。 本節將要探討指標與陣列間的微妙關係,以及兩者在程式中相互應用的方式。
45
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-2-1 一維陣列指標 5-2 指標和陣列
46
5-2-1 一維陣列指標 我們已經知道陣列經過宣告後,會取得記憶體空間的配置。 存取陣列中的元素時,語法如下:
利用起始位址指定的陣列名稱,可以把它看作是一個指向陣列第一個元素的指標,透過指標方式,則上面的語法可視為:
47
常數指標的概念 在此要補充一點:這個指標是一個「常數指標」,在程式中並不能改變其值。
例如底下這三行敘述試圖改變陣列指標所指位址,編譯時將會產生錯誤:
48
以取值運算子(*)取陣列元素 雖然無法改變陣列指標的值,既然知道其所指的位址為陣列第一個元素的位址,
利用取值運算子「*」便可以取出陣列中元素的資料值,敘述如下:
49
陣列與指標的關係 假設陣列的名稱為value,則value是指向陣列第一個元素的常數指標。
50
取得陣列元素的位址值
51
範例:ptrvalue.cpp
52
執行程式:ptrvalue.cpp 第7行宣告一個浮點數型別的陣列,並設定起始值。
第12~17行利用for迴圈來讀取陣列的元素。其中第14行輸出陣列的儲存值,第16行則是利用指標運算方式來輸出記憶體位址。
53
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標
54
5-2-2 二維陣列指標 我們已瞭解了一維陣列指標的意義,接下來介紹二維陣列指標?學習如何運用指標計算的概念,可以得到如下的處理方式:
指向的記憶體位址當中 也包含一個指向一維陣列元素指標的位址 二維陣列指標 是一個「指標中的指標」 簡單的說
55
二維陣列指標示意圖. 為了簡化概念,我們以下圖來釐清問題:
56
二維陣列指標示意圖.. 二維陣列指標示意圖是一個3x3二維陣列指標。 因此,若要藉由value來取出二維陣列中的所有元素值
陣列中的各個指標則再指向value陣列中的三個元素value[0][0]、value[0][1]、value[0][2] 同理,若value+1是指向下一個指標陣列,則其中的各個指標則指向value中的下三個元素value[1][0]、value[1][1]、value[1][2],這便是二維陣列「指標的指標」的概念 因此,若要藉由value來取出二維陣列中的所有元素值 內層括號(value + n)是取得相對於vaule下n格的位址(各個指標陣列的起始位址),前面所加的*運算子可用來取得該位址中所儲存的第一個「指標元素」。 而在外層括號中則利用「+」來取得指標陣列中其它的指標元素,並在最前面加上*運算子來取得各個指標所指位址中的儲存值。
57
範例:2dptr.cpp(1)
58
範例:2dptr.cpp(2)
59
執行程式:2dptr.cpp
60
程式解說:2dptr.cpp 利用指標運算來取出二維陣列中的所有元素,這表示必須利用二層for迴圈來分別讀取陣列的元素。
61
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-5 指標的移動 5-1-4 以new初始化指標 5-1-6 一般型別系統中的指標 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3 字串和指標
62
5-3 字串與指標 討論過陣列指標後,另一個和陣列習習相關的是「字元」。
我們已經知道字元陣列組成字串,那麼是否可以利用指標的觀念來處理字串,此為本節要探討的內容。
63
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3-1 使用字串指標 5-3 字串和指標
64
5-3-1 使用字串指標 首先,複習字串的宣告方式,敘述如下:
我們必須知道,以字元陣列宣告字串時,必須設定陣列長度以容納字元。當字串長度超過所設定的陣列大小時,編譯時則會發生錯誤。
65
以字串指標來定義字串 另一種宣告字串的方法是使用「字串指標」直接來定義一個字串,以字串指標定義字串的敘述如下:
使用字元指標時會指向第一個儲存字元的記憶體位址。就上例而言,字元指標str2會指向儲存字元「I」的記憶體位址,且利用字串指標來定義字串時也不須預設字串長度。 此外,指標的值也可以改變,也就是指標所指的位址可以前後移動。同樣的我們可以利用陣列指標來取得字串中各個字元的值。
66
字元陣列和字串指標示意圖 無論是字元陣列或是字串指標都可以用來宣告字串變數,那麼它們在記憶體中分佈的情形是否相同!以下圖說明:
67
範例:stringptr.cpp
68
執行程式:stringptr.cpp 第7行以字元陣列方式宣告第一個字串;第8行則以字串指標來宣告第二個字串。
第12~15行透過while迴圈來讀取字元陣列的字元。迴圈的條件為「*(str1+i) != NULL」情形下,執行第13行顯示字元與第14行遞增index值的程式碼。若取出的字元為NULL值時,表示已到字串的結尾。由於str1為陣列指標,因此無法移動它,只能利用遞增i值來取得相對於該指標所指位址的其它位址,並取得儲存在這些位址中的字元資料。 第20~23行以while迴圈讀取第二個字串的字元,第21行直接以指標方式輸出結果。
69
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-1-4 以new初始化指標 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3-1 使用字串指標 5-3 字串和指標 5-3-2 字串指標陣列
70
5-3-2 字串指標陣列 使用字串指標來宣告字串的好處是不必預設字串長度,也不會浪費所佔用的記憶體空間。
因為若以字元陣列來設定字串必須預設陣列的大小,如果所設定的陣列長度大於字串實際的長度,或多或少都會形成記憶體的浪費。 此外,如果是用字元陣列來宣告字串陣列,必須使用二維陣列,如下面敘述:
71
以二維陣列定義字串的缺點 以二維陣列定義字串的缺點
上述敘述中,我們發現,為了能讓陣列存放「Philip」這個字串,必須將欄註標宣告為「7」 雖然「Mary」與「Bill」的字元長度只佔用了「5」個字元,但系統卻配置相同的字元長度 這種以二維陣列定義字串,其缺點就是徒增記憶體的浪費 為了解決這樣的問題,使用「字串指標陣列」,讓其中的各個指標元素指向每一個字串是最佳的方法。 如此一來,配置的記憶體長度就不必預設,系統會自動依字串的大小來配置適當的記憶體空間。 利用陣列中的各個指標元素,還是可以取得各個字串的內容。
72
以字串指標宣告字串 M B P 以字串指標宣告三個字串的方式:
「str」在這裡是一個字串指標,其中的各個指標元素則分別指向三個字串所在的記憶體位址。 ary M ill B hilip P 指標str[0]指向「Mary」字串「M」字元的儲存位址 指標str[1]指向「Bill」字串「B」字元的儲存位址 指標str[2]指向「Philip」字串「P」字元的儲存位址
73
範例:2dstr.cpp
74
執行程式:2dstr.cpp 本範例用來說明透過字串指標陣列,可以減少記憶體空間的浪費。 第8行宣告一個二維字串指標陣列。
第9~20行以for迴圈來讀取3個字串。 第13~17行則利用while來設定條件運算式,檢查是否已到字串結尾\0字元,如果沒有則利用第15行來輸出每一個字串的字元。 第18行的k值必須重新宣告為0,因為k值只存在while迴圈,離開while迴圈要重新讀取時必須重新設定。
75
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-1-4 以new初始化指標 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3-1 使用字串指標 5-3 字串和指標 5-3-2 字串指標陣列 5-4 指標應用
76
5-4 指標應用 學會了前面所介紹的幾種基本的指標類型之後,本節將討論指標的進階類型,包含
動態指標 指標的指標(函式指標參考) 接下來我們就來看看這兩種類型的指標在程式中的宣告與使用方式。
77
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-5 指標的移動 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3-1 使用字串指標 5-3 字串和指標 5-3-2 字串指標陣列 5-4-1 建立動態陣列 5-4 指標應用
78
5-4-1 建立動態指標 使用陣列時,必須事先指定陣列的長度,為了節省記憶體空間可利用指標運算。
可是有時候我們會遇到一種狀況:使用的陣列能否在執行時才決定該陣列的大小,這種情形便無法在宣告時先設定陣列的大小。
79
動態記憶體配置 當然我們可以在宣告陣列時,把陣列長度設得越大越好 執行時才決定陣列大小的方法稱為「動態記憶體配置」。
但有可能會發生宣告的陣列大小比實際所需的大小還大的情形,造成記憶體的浪費。 還記得前面所介紹的new運算子,加上指標的配合,便能解決上述的問題。 執行時才決定陣列大小的方法稱為「動態記憶體配置」。 在動態記憶體配置下所宣告的變數無法在編譯時做決定,通常要透過指標所指向的記憶體位址。
80
動態陣列的宣告 我們利用new來宣告動態陣列,語法如下: 利用new來宣告一個動態陣列時 第1行宣告了指標的資料型別
第3行則是把第1、第2合併,以設定起始值方式來完成動態陣列的宣告。
81
以delete釋放記憶體 當我們完成動態陣列的使用後,必須利用delete來進行記憶體的釋放,語法如下:
瞭解了以指標動態建立陣列的方式之後,我們利用範例程式「searchnum.cpp」讀取使用者輸入的數字,並建立陣列來儲存各個數字,當使用者輸入欲搜尋的數字時,能顯示它是第幾個數字。
82
範例:searchnum.cpp(1)
83
範例:searchnum.cpp(2)
84
執行程式:searchnum.cpp 範例是一個簡易的循序搜尋 先設定欲處理的多少數字,再輸入數值
然後輸入欲搜尋的數值,再顯示它是第幾個數字
85
程式解說:searchnum.cpp(1) 第8行先將變數tag設為「-1」,它的作用是存放陣列的索引值,所以必須先進行設定
第10是用來決定陣列的大小,如果輸入3,表示陣列維度為3,那麼第12行敘述得連續輸入三個數值 第13行依據第10行輸入的陣列大小來產生一個動態陣列 第16~18行利用for迴圈來存放使用者輸入的數字,第18行讀取時以指標運算方式來處理 第22行輸入欲搜尋的數字,並利用第23行number變數來存取
86
程式解說:searchnum.cpp(2) 第25~30行利用for迴圈來依序讀取陣列的元素,並以第26行指標運算來判別與使用者輸入的number是否相等!如果相等,將陣列索引值指派給在第27行的tag變數,並中斷迴圈的執行 第31~35行用來判斷tag的索引值是否有找到相等的數字,確實有找到,在第34行輸出結果,說明是輸入的第幾個數字 第36行,可別忘了要進行記憶體的釋放
87
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-4 以new初始化指標 5-1-5 指標的移動 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3-1 使用字串指標 5-3 字串和指標 5-3-2 字串指標陣列 5-4-1 建立動態陣列 5-4 指標應用 5-4-2 CLR與動態陣列
88
5-4-2 CLR與動態陣列 利用new來建立動態陣列是一個好方法。
本身是一個關鍵字 array 是資料型別,指的是符合共通語言規範(CLI),包含原生型別(type*),列管型別(type)和列管參考型別(^) type1 也是資料型別,用來初始化的陣列,因此後面接著的()用來設定陣列大小 type2 用來取代原有的new運算子 gcnew
89
按下「確定」鈕之後,它會完成專案的建立,並產生相關的部份程式碼,然後我們針對【NewArray.cpp】修改如下:
建立主控台應用程式 我們利用專案精靈來建立一個CLR主控台應用程式。步驟如下: 執行「檔案/新增/專案」指令 選取「CLR主控台應用程式」 輸入專案名稱「NewArray」 按「確定」鈕 按下「確定」鈕之後,它會完成專案的建立,並產生相關的部份程式碼,然後我們針對【NewArray.cpp】修改如下:
90
範例:NewArray.cpp
91
執行程式:NewArray.cpp 範例本身很簡單,就是設定陣列元素,然後輸出內容 只不過它使用的CLR撰寫方式,看起來較為陌生
以「F7」來建置專案,「Ctrl + F5」來執行專案
92
程式解說:NewArray.cpp 第4行要宣告的名稱空間是System。當我們透過.NET Framework架構來運作程式時,就必須以System為名稱空間 第6行還是main()主程式。在CLR下,它是以Unicode來傳遞。如果沒有進行參數傳遞時,這些引數可以省略 第8行建立一個陣列大小為2的動態字串。然後在第9、10行設定陣列元素 第12行則是將結果輸出於螢幕,C++是使用cout物件,此處是使用Console類別將欲輸出字串利用WriteLine()方法來處理。此處我們指定輸出第一個陣列元素。 這裡只是先讓大家瞭解在CLR下,撰寫程式的方法略有不同,在後面章節還會向大家介紹最多Visual C 一些新的作法。
93
章節目錄 5-1-1 認識記憶體位址 5-1-2 宣告指標 5-1-3 指標初始化 5-1 指標的宣告和使用 5-1-4 以new初始化指標
5-1-6 一般型別系統中的指標 5-1-4 以new初始化指標 5-1-5 指標的移動 5-2-1 一維陣列指標 5-2 指標和陣列 5-2-2 二維陣列指標 5-3-1 使用字串指標 5-3 字串和指標 5-3-2 字串指標陣列 5-4-1 建立動態陣列 5-4 指標應用 5-4-2 CLR與動態陣列 5-4-3 指標的指標
94
5-4-3 指標的指標 「指標的指標」顧名思義就是一個指標指向記憶體中包含資料的位址,而另一個指標再指向該指標,其示意圖如下:
使用時機就如同前面介紹字串指標時用來建立字串陣列,以及動態建構二維陣列 本節裡我們將說明「指標的指標」宣告與使用的方式。
95
宣告指標的指標 在上述敘述中,第3行敘述宣告指標的指標ptr_ptr時,多使用了一個「*」運算子。 表示ptr_ptr是指向指標的指標
設定ptr_ptr指向的位址為ptr_value的儲存位址(&ptr_val)。
96
取出指標的指標 如果要取出「指標的指標」指向「指標」所指向的位址值,亦必須加上使用一個「*」取值運算子,敘述如下:
第1個 取值運算子 取出ptr_ptr中的位址 即為指向ptr_value指標的儲存值 (儲存value位址) 第2個 取值運算子 取出向ptr_value指標所指向位址的儲存值(value的儲存值)。
97
字串變數的儲存圖 字元陣列設定起始值時,與一般陣列的起始值設定是相同的。
我們可以將字串變數視為一維陣列,它只能儲存一個名稱,如果要利用字串變數儲存多位學生的姓名時,就必須以字串陣列來運作。
98
範例:Toptr.cpp(1)
99
範例:Toptr.cpp(2)
100
執行程式:Toptr.cpp 表示它是一個指向ptr_value指標的儲存值,儲存變數value的位址
**ptr_to則會取出ptr_value指標指向value的儲存值
101
程式解說:Toptr.cpp 第10行宣告了一個整數型別的value變數。第11行宣告了指向整數的指標ptr_value。第12行宣告了一個指向ptr_value指標的指標ptr_to。 第17、20、23行用來分別輸出value、ptr_value、ptr_to的位址和儲存值。 第27、28行則是輸出*ptr_to和**ptr_to的內容。 我們可以比對輸出結果,因為ptr_to指向ptr_value指標,所以它的儲存值是ptr_value的位址,而ptr_value指標又指向value變數,因此ptr_value的儲存值是value的位址。 *ptr_to是一個指向ptr_value指標的儲存值,ptr_value儲存的是變數value的位址。 **ptr_to則是取出ptr_value指向value變數所儲存的內容「20」。
102
value、ptr_value和ptr_to三者關係
範例程式「Toptr.cpp」中value、ptr_value和ptr_to三者的關係,如下圖5-9所示
103
重點整理. 宣告一般變數時,記憶體會畫出一塊空間來存放變數的資料 「指標」在C++中是一個指向某個記憶體位址的特殊變數
記憶體的每一塊空間都會有自己的編號,我們稱為『位址』(Address) 「指標」在C++中是一個指向某個記憶體位址的特殊變數 宣告時要在變數名稱上加上「*」取值運算子(Dereference operator) 指標變數宣告後,必須先進行初始化的動作 未經初始化的指標,有可能會覆寫到任意的記憶體區塊,產生無法預估的錯誤! 使用「&」取址運算子來取得變數在記憶體中的位址,透過「*」取值運算子來取得指向記憶體內的儲存值。
104
重點整理.. 利用new運算子直接初始化指標,配置所需的記憶體,並設定指標所指向記憶體位址的儲存值,再以delete運算子來釋放記憶體空間。
「指標的移動」可視為指標在記憶體內指向位址的改變,其移動值是以指標宣告資料型別所佔用的位元組(Bytes)來計算,控制指標的移動,便可取出對應記憶體中的資料值。 .NET Framework提供三種指標: 列管指標(Managed pointers) 未列管指標(Unmanaged pointers) 未列管函式指標(Unmanaged function pointers)
105
重點整理… 在一維陣列指標中,可利用「*」取值運算子來取得陣列中的元素。如果要取得陣列元素的位址值 二維陣列指標是一個「指標中的指標」
第一種方法是利用「&」取址運算子 第二種透過指標的運算 二維陣列指標是一個「指標中的指標」 它所指向的記憶體位址當中,也包含一個指向一維陣列元素指標的位址。 使用字元指標時會指向第一個儲存字元的記憶體位址,由於指標所指的位址可以前後移動,所以可以利用陣列指標來取得字串中各個字元的值。
106
重點整理…. 使用字串指標陣列來宣告字串的好處是不必預設字串長度,也不會浪費所佔用的記憶體空間,讓其中的各個指標元素指向每一個字串。
所謂「動態記憶體配置」是執行時才決定陣列大小。 在動態記憶體配置下所宣告的變數無法在編譯時做決定,通常要透過指標所指向的記憶體位址。
Similar presentations