# Pointer basic --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> # 前置作業 確認電腦中有安裝`git`和`gdb` 如果有缺請用`sudo apt-get install` 安裝 並執行以下指令: ``` git clone https://github.com/HMKRL/lion-pointer.git cd lion-pointer/ ``` --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> # gdb ## (GNU Debugger) ---- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> 安裝 ![](https://i.imgur.com/boIGEpb.png) 啟動 ![](https://i.imgur.com/6qjBqw7.png) ![](https://i.imgur.com/l4Rgycs.png) ### 使用時編譯參數需加上 `-g` ![](https://i.imgur.com/Csu7rw8.png) ---- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> ## 基本指令 |指令 |用途 |簡寫用法| |:--:|:--:|:--:| |`file <執行檔名稱>` |讀入執行檔| |`run`|程式開始執行|`r`| |`list`/`list 行數`|顯示程式碼|`l`| |`break`|設定中斷點|`b`| |`continue`|程式繼續|`c`| |`print 變數名稱`|印出變數內容|`p`| |`quit`|離開|`q`| |`whatis`|顯示type| ---- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> ![](https://i.imgur.com/egvU8Tf.png) ![](https://i.imgur.com/BiXiU3w.png) ---- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> # GDB Practice 請使用gdb 對bug.c進行除錯 --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> ![](https://i.imgur.com/WoYoIoR.png) ---- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> # Call by value 在C語言中,函式呼叫時會把變數複製一份傳入 因此傳入的只有變數的"__數值__"! --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> # 變數儲存 ![](https://i.imgur.com/ZESyCjM.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> `int var = 39;` ![](https://i.imgur.com/mkfsjDe.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ![](https://i.imgur.com/eDM0Sus.png) --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> # 變數型態-指標 指標並不是什麼神祕的東西 它和`int`或`char`一樣都是一種變數型態 ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## 宣告方式 ### `type * 變數名稱` ![](https://i.imgur.com/MPKCuG2.png) 此處的星號代表『指標型態』 前面的type則是『指標指向的type』 ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## Practice ![](https://i.imgur.com/UKEUQ7p.png) p b c d 分別是什麼type? ---- ## 使用whatis ![](https://i.imgur.com/sQNMoZy.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> # 指標用運算子 ## `* (value of)` ## `& (address of)` ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## 指標變數賦值 #### `int *ptr = &var` #### `ptr = &var` #### `ptrA = ptrB` <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ---- # practice #### 宣告一個`int`變數 `a = 3` #### 和一個指標變數 `ptr` 指向 `a` #### 用 gdb 觀察`ptr`的值 --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> ## `int var = 39` ![](https://i.imgur.com/mkfsjDe.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## `int *ptr = &var` ![](https://i.imgur.com/7pzv2bB.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## `*ptr = 8763` ![](https://i.imgur.com/knGs9TK.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## `int *ptr2 = ptr` ![](https://i.imgur.com/7p27kDp.png) ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> # `*ptr2 = ?` ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> ## practice 修改swap.c到正常運作 正常輸出: ![](https://i.imgur.com/IOKoflD.png) --- <!-- .slide: data-transition="slide" data-transition-speed="fast"--> ## 為何要知道指標指向的type? ---- <!-- .slide: data-transition="zoom" data-transition-speed="fast"--> `int pi = 31415926` ![](https://i.imgur.com/lcCQjH4.png) `float f = 2.813587e-09` ![](https://i.imgur.com/lcCQjH4.png) `short s = 12609` ![](https://i.imgur.com/53Be2Ro.png) ---- ## `&pi = ?` ## `&f = ?` ## `&s = ?` ---- ## 全部都是 `0xc8763`!! ![](https://i.imgur.com/6Yr9p59.jpg) ---- #### 如果不知道指標所指向的記憶體存了什麼type #### 就沒辦法正確解讀記憶體內容 ![](https://i.imgur.com/1qQPtUf.png) --- ## 知道type後除了可以解讀 ## 還有什麼作用? ---- ## 指標加減運算 #### 編譯pointer_add.c,用GDB印出 #### (1)`intptr`, `intptr + 1` #### (2)`charptr`, `charptr + 1` ---- ![](https://i.imgur.com/NYqKyw1.png) ![](https://i.imgur.com/5eitWoM.png) ---- ## 對指標來說,加法/減法的意義是移動本身type的大小! ---- ![](https://i.imgur.com/ZuUT3aO.png) ---- ![](https://i.imgur.com/XVGtwB2.png) ---- ## Practice 宣告一個`char`陣列 ![](https://i.imgur.com/di2EE3O.png) 使用gdb觀察 `ch`、`ch[0]`到`ch[3]`的記憶體位址 ---- ## 有何發現? ---- ![](https://i.imgur.com/pQ5PWDq.png) ---- ## 在C語言中 陣列就是連續的記憶體位置! ![](https://i.imgur.com/cMQyndE.png) ---- ## Practice 修改pointer_array.c 不使用[]印出陣列內容 ---- ## 由此可知 ## `arr[3]` 和 `*(arr + 3)` ## 是一樣的! --- ## 所以陣列就是指標...嗎? ---- ### 很明顯不一樣... ![](https://i.imgur.com/7xlyfR0.png) ---- # 可是到底哪裡不一樣? ---- ## 陣列會包含尺寸資訊 ![](https://i.imgur.com/EXmnxof.png) ![](https://i.imgur.com/HREnR98.png) ---- ### 陣列和指標在以下情況可以通用: * 不用於宣告時 ```clike int a[10]; int *ptr = a ``` * 用`[]`取內容時 ```clike `a[3] 和 *(a + 3)同義 ``` * 傳入function時 ```clike void function_name(char *arr) ``` ```clike void function_name(char arr[]) ``` #### 相同 ---- ## Quiz #### 如何在function中知道array有幾個元素 ---- ## Ans #### 沒辦法 ; _ ; ---- ### 剛剛提過,傳入function時array和指標相等 編譯器只會把array的開頭指標傳進function 如果要知道array大小,只能自己傳進去~ 這個情況稱作 __退化__ __(Pointer decay__ ``` void function_name(char *arr, int size) ``` ---- ## Practice: 寫一個印出array所有元素的function,陣列大小需用傳入的 ```clike= void print(/*the parameter you need*/) { int i; for(i = 0;i < array_size;i++) { /* code for print array*/ } } ``` --- ## NULL Pointer `int *ptr = NULL` NULL用來代表『這個指標沒有指向特定變數』 所以也不能做*ptr 用途:之後做linked-list時判斷用 ---- ## 指標強制轉型 前面提過, 知道指標型態才能正確解讀記憶體內容 ---- ### 我們也可以利用強制轉型(casting)操作指標 ### 做一些原本不能做的事 #### (例如對float做>>) ---- 以`int`形式讀取一個`float`變數 ![](https://i.imgur.com/AJO3RaR.png) ![](https://i.imgur.com/3mJdyFU.png) ---- ## Practice 印出浮點數的bit pattern(使用pointer casting) ![](https://i.imgur.com/xzhKWiQ.png) --- ## 指標應用? ---- ![](https://i.imgur.com/9mOXWKW.png) ---- `char`的陣列其實就是字串! 但最後會有一個字元`'\0'`代表結束 ---- |0|1|2|3|4| |:-:|:-:|:-:|:-:|:-:| |`C`|`S`|`I`|`E`|`\0`| ---- ## Practice 寫一個function `string_length` 計算argv[0]的長度 ---- const pointer ```clike= #include <stdio.h> int main(int argc, char *argv[]) { int a = 3, b = 2; int *ptr1=&a; //ptr 和 *ptr都可以改 const int *ptr2 = &a; //ptr2可以改, *ptr2不能改 int * const ptr3 = &a; //ptr3不能改, *ptr3可以改 const int * const ptr4 = &a; //都不能改 return 0; } ``` ```