890 likes | 1.09k Views
EL 1012 程式設計. Program Design. Instructor : Po-Yu Kuo 教師 : 郭柏佑. Ch. 12 指位器 (pointer). 本章簡介. 指位器 (pointer) 也是一種變數 , 但是此種變數儲存的並非一般數值 , 而是記憶體位址。 當指位器變數所存的值是位址時 , 我們稱此指位器指向該位址所表示的記憶體空間。 指位器可以用來表示複雜的資料的結構 , 或者有效率的存取陣列的資料。. 記憶體位址. 記憶體位址.
E N D
EL 1012程式設計 Program Design Instructor:Po-Yu Kuo 教師:郭柏佑 Ch. 12 指位器 (pointer)
本章簡介 • 指位器 (pointer) 也是一種變數, 但是此種變數儲存的並非一般數值, 而是記憶體位址。 • 當指位器變數所存的值是位址時, 我們稱此指位器指向該位址所表示的記憶體空間。 • 指位器可以用來表示複雜的資料的結構, 或者有效率的存取陣列的資料。
記憶體位址 • 但是, 因為使用指位器的過程中, 會涉及許多記憶體位址與空間的概念, 造成不了解此種概念的讀者學習上的困難。 • 指位器是專門用來存放記憶體位址的變數。 • 我們可以把記憶體的內部, 想像成一個一個排列整齊可用來裝填資料的小格子, 每個小格子的大小都相等 (1 byte)。
本章簡介 • 我們可以利用 & 算符, 找出變數在記憶體中被配置的位址, 語法如下: • 在任何變數名稱前加上 & 算符後, 就表示該變數在記憶體中的位址, 而不是變數值。 • 利用 printf ( ) 配合輸出格式 %p, 就可以輸出以 16 進位數值表示的記憶體位址, 如下:
記憶體位址 • 因為 4 個變數都是整數型別, 所以在記憶體中各佔 4 bytes。 • 在記憶體中, 每個 byte 都有獨立的位址, 以代表該空間在記憶體中的位置。
記憶體位址 4 bytes
指位器的基本用法 • 指位器的運用, 主要是在一種稱為『指向』的觀念。 • 當指位器中儲存著一個變數的位址時, 我們稱此指位器指向該變數。 • 宣告的語法如下:
指位器的基本用法 • 資料型別:指位器的型別, 必須與該指位器所指向的變數型別相同。 • *:稱為『間接』算符 (indirection), 意指指示器存放的是儲存變數的位址, 不能直接取得變數的值, 必須在指位器上使用間接算符才能取得變數值。 • 宣告指位器變數時, 需在變數名稱前加上此算符。
指位器的基本用法 • 指位器:也就是指位器變數的名稱, 命名的原則和一般變數相同。 • 例如:
指位器的基本用法 • 如此一來, ptr 便是指向 number 的指位器了。
指位器的空間大小 • 由於指位器記錄的是記憶體位址, 所以不管指位器宣告成何種型別, 在記憶體中都會配置相同的空間 (4 bytes), 我們可以利用 sizeof ( ) 來驗證, 如下:
設定指位器的值 • 指位器也是一個變數, 所以和變數一樣可以設定其值。 • 如果我們將指位器採用與變數相同的設定方式, 如下:
設定指位器的值 • 由於 ptr 本身未被設定初始值, 所以其值有可能是任何數值, 換言之, ptr 可能指向任何的記憶體空間。 • 例如其它程式、甚至作業系統正在使用的記憶體空間。此時若將數值 35 指定到此不知位於何處的記憶體空間, 有可能導致程式執行發生錯誤。 • 所以, 指位器不能使用上述的方法來設定其值。
設定指位器的值 • 當我們要將數值指定給指位器時, 我們需要以下面的步驟來完成: • 1.事先宣告一個同型別的變數, 再讓指位器指向此變數位址: • 2.完成上述指定動作之後, 就可以利用 * 算符, 從指位器中取出數值。也就是說 *ptr 的值會等於 10。
設定指位器的值 • 上面步驟完成後, 指位器與變數間的關係如下圖: • 我們先宣告 3 種不同型別的指位器, 讓其分別指向 3 個變數, 然後輸出其值。如下:
設定指位器的值 將指位器指向變數
指位器的轉型 • 指位器只能指向同型別的變數位址, 絕對不能有以下的情形: • 若想將指位器指向不特定型別的變數, 可使用 void 這個特殊的型別來宣告指位器變數。 • void 型別的指位器可指向任何型別。
指位器的轉型 • 但要取用指位器所指的變數值時, 必須做強制型別轉換才能取出正確的值:
指位器的運算 • 因為指位器內儲存的是位址, 所以對指位器做加減, 就等於是將所存的位址做加減, 所得到的將會是鄰近的位址。
指位器的運算 • 對 ptri 而言, 加減 1 時, 位址值是加減 4 (bytes); 但對 ptrc 加減 1 時, 位址值則只加減 1 (bytes)。 • 這是因為對指位器做加減時, 是以指位器型別所佔的 byte 數為單位進行加減, 所以 int 型別一次就是加減 4、char 型別一次是加減 1。 • 同理 float、double 型別的指位器加減時, 位址值分別是加減 4、8 (bytes)。
指位器的運算 • 要取得指位器鄰近位址內的值, 可直接在前面加上 * 算符, 如下:
指位器的運算 • 由於 * 算符的優先權高於算術算符, 所以我們要在 ptri+1 外面加上括號, 否則會變成 *ptri 的值再加上 1。 • 注意, 由於指位器的值是變數的位址, 所以任意兩個指位器相加減, 等於是兩個記憶體位址相加減, 其結果是沒有意義的。
使用指位器的好處 • 變數可以直接儲存數值, 所以需要數值時可以直接讀取變數值。 • 而指位器儲存的卻是存放變數值的記憶體位址, 所以如果要讀取數值時, 必須透過指位器內儲存的位址去尋找記憶體中位址的空間, 才能讀到數值。 • 但是,指位器的用途並非只是單純的存取數值, 指位器的主要用途如下:
使用指位器的好處 • 指位器在某些方面可以代替陣列的使用。 • 陣列是在記憶體中一段被編譯器配置的連續空間, 也就是說這些空間的位址是連續的。 • 如果採用指位器儲存位址的觀念來看, 我們把指位器作加減的運算, 也是可以讀取到儲存在連續記憶體空間內的資料。
使用指位器的好處 • 指位器適用函式間的傳址運算。 • 當函式間需要傳遞數值時, 如果使用指位器, 傳遞到函式中的引數不再只是將數值複製到函式中做處理, 而是將數值儲存的位址傳到函式, 由函式直接存取。 • 此時, 就算函式無傳回值, 引數的變數值也會經過函式的運算而有所改變, 這種做法可以改善函式只能有一個傳回值的缺點。
使用指位器的好處 • 有些特殊的資料結構 (Data Structure) 及相關演算法 (Algorithm), 都需利用指位器才能順利實作。
動態記憶體配置 • 甚麼是動態記憶體配置? • 當我們在程式開始宣告一個變數時, 編譯器就會所需的記憶體空間來存放這個變數的值。 • 只要在程式執行期間, 該變數會佔用一塊固定大小的記憶體空間, 就算程式想臨時擴大或縮小佔用的的空間也不行, 所以稱之為靜態記憶體配置。
動態記憶體配置 • 動態記憶體配置則是:當程式執行到一半, 發現它需要一塊記憶體空間來存放資料, 才向系統索取一塊沒有被其他程式使用的記憶體空間。 • 當此記憶體空間用不到時, 也可隨時將之釋放供其它程式使用, 如此可大幅的提高記憶體的使用效率。
動態記憶體配置 的語法 • 向系統索取記憶體區塊主要是透過 malloc ( ) 函式來做, 此函式的原型宣告放在stdlib.h, 使用時要將此含括檔含括進來。呼叫的語法如下: • 資料型別:新配置空間的型別。 • 個數:新配置多少同型別的變數空間。
動態記憶體配置 的語法 • 如果記憶體空間不夠分配時, 此函式會傳回 NULL 。 • 記憶體配置成功後, 則會傳回所配置記憶體空間的起始位址, 所以我們要先宣告一個指位器來接受傳回的位址。如下:
動態記憶體配置 的語法 • 上面的範例, 是初始化動態記憶體配置的寫法, 也就是在宣告指位器的同時, 便配置記憶體空間。 • 配置成功後, 使用 num 和 code 時, 就和使用以 int num [10]; 與 char code [5]; 取得的陣列記憶體空間一樣, 可任意儲存資料於其中。 • 空間使用完畢, 可用 free ( ) 函式將配置的記憶體釋放。
動態記憶體配置的特色:有需要, 才配置 • 動態記憶體配置的主要特色, 是我們不必在程式開始就先配置記憶體空間。 • 而是可在程式流程執行到一半, 發現有需要時, 才配置所需的空間。例如:
動態記憶體配置的特色:有需要, 才配置 • 不管是哪種配置方式, 記憶體空間使用完畢後, 一定要以 free ( ) 的語法將配置的記憶體空間釋放, 這是一個很重要的習慣, 可以讓記憶體的使用更具效率。 • 由於寫程式時無法預知使用者想計算多少個數字。 • 因此可設計成在使用者輸入數字個數後, 再動態配置所需的記憶體空間來存放數值。程式如下:
動態記憶體配置的特色:有需要, 才配置 使用者輸入數字個數 n 動態配置 n 個記憶體空間
指位器與陣列 • 指位器儲存的是記憶體的位址, 而陣列是記憶體中一段位址連續的記憶體空間。 • 用指位器代替陣列 • 使用動態配置的空間來儲存資料 • 使用指位器來儲存字串 • 指位器陣列 • 自行調整字串長度 • 利用指位器陣列作字串排序
用指位器代替陣列 • 雖然指位器是用來儲存位址的變數, 但我們可以採用動態記憶體配置的方法, 配置一塊記憶體空間給指位器。 • 因為我們可以設定配置多少空間, 就如同宣告陣列時, 設定陣列容量一樣。 • 使用動態記憶體配置配置一塊空間, 並讓指位器 ptr 指向此空間時。
用指位器代替陣列 • 若把這個空間看成是個陣列, 則 ptr+i 就相當於陣列中, 第 i 個陣列元素的位址。 • 並且可以利用 *(ptr+i) 的方式, 取得取得第 i 個陣列元素的數值。 • 如以下範例中, 我們將利用指位器代替陣列, 儲存 5 個由鍵盤輸入的數值, 並找出最大值:
使用動態配置的空間來儲存資料 動配置 5 個記憶體空間的位址 利用 *(ptr+i) 的方式, 取得第 i 個陣列元素的數值