--- title: 指標篇 心得 tags: C 語言筆記 --- ## 指標篇 心得 ### 例子: ``` void **(*d) (int &, char **(*)(char *, char **)); ``` * d 是 pointer to a function 並且回傳 pointer to pointer to void * 參數一: reference to int * 參數二: pointer to function 並且回傳 pointer to pointer to char * 參數一: pointer to char * 參數二: pointer to pointer to char ### C 語言只有 call-by-value * ptr 是個 pointer type,對 ptr++ 來說,並不是單純 ptr = ptr + 1,而是遞增或遞移 1 個「單位」 * ex: int* ptr -> ptr++ 的單位就是 4 byte ```c int main(void) { int a[2] = {1, 2}; int* p = a; printf("%p\n", p); printf("%p -> %p\n", p, p++); } ``` 輸出: ``` 0x7fffc405fd40 0x7fffc405fd44 -> 0x7fffc405fd40 ``` 觀察 printf 和 i++ 、 ++i `printf` 是由右往左開始執行的 `++i` 會先將 `i` 加一後回傳變數 `i` `i++` 則是會先回傳 `i` 這個變數的==值== ```c #include <stdio.h> int main(){ int i=1; printf("%d %d %d %d", i++, i, ++i, i); return 0; } ``` 輸出: ``` 2 3 3 3 ``` ### (練習題) 設定絕對地址為 0x67a9 的 32-bit 整數變數的值為 0xaa6,該如何寫? * 絕對位址 --> (int* const) (0x67a9) * assign 值進去: * (int* const) (0x67a9) = 0xaa6 ### void * 和 char * 彼此可互換的表示法 ``` void *memcpy(void *dest, const void *src, size_t n); ``` ### 函式呼叫只有 call-by-value & 指標的指標 ```c= int B = 2; void func(int *p) { p = &B; } int main() { int A = 1, C = 3; int *ptrA = &A; func(ptrA); printf("%d\n", *ptrA); return 0; } ``` 以上程式碼無法成功改到 *ptr 的值,因為函式是 call-by-value 所以運用 指標的指標 改成 ```c= int B = 2; void func(int **p) { *p = &B; } int main() { int A = 1, C = 3; int *ptrA = &A; func(&ptrA); printf("%d\n", *ptrA); return 0; } ``` ### Pointers vs. Arrays * array 與 pointer 可互換 * x[i] 總是被編譯器改寫為 *(x + i) ← in expression * 所以 x[4] *(x + 4), *(4 + x), 4[x] 可以達到相同的效果 * &b+1 -> 位移一個siezof(b) * &b[0]+1 -> 位移一個sizeof(b[0]) * concatenate string s and string t * 需要配置足夠的記憶體 * 記得釋放 * malloc可能執行錯誤 ```c= char *r = malloc(strlen(s) + strlen(t) + 1); if (!r) exit(1); /* print some error and exit */ strcpy(r, s); strcat(r, t); free(r); r = NULL; /* Try to reset free’d pointers to NULL */ ``` * array 會是用兩步取值,而 pointer 是三步。(array 的位址本身加上 offset,共兩步,而使用 pointer時,cpu 需先載入 pointer 位址,再用 pointer 的值當作位址並加上 offset 取值) ### Function Pointer * 函式只有再搭配 &, sizeof 時才不會被轉成 pointer to function * 例子 ``` int main() { return (********puts)("Hello"); } ``` * puts -> function designator * *puts 的 puts 會被轉成 &puts (pointer to function returning type) * 最後變成 *(&puts) , 最後結果等同於 function designator (puts) * 所以上述例子等同 ``` int main() { return (puts)("Hello"); } ``` ### Address and indirection operators * &(a[5]) 等同於 a + 5 * char str[123] * str 等同於 &str * str 因為不是遇到 sizeof 或 &,所以會被解讀成 pointer of type(這邊是 char) * &str 就是 pointer to an array,剛好就是指向 str 的起始位址 ### 指標的修飾 (qualifier) * 指標本身不可變更: const 在 * 之後 ``` char * const p; ``` * 指標所指向的內容不可變更 (Pointer to constant): const 在*之前 ``` const char * p; char const * p; ``` * 兩者都不可變更: ``` const char * const p; ``` * const int*ptr1 ![](https://hackmd.io/_uploads/HyOFZHgFh.png) * int* const ptr2 ![](https://hackmd.io/_uploads/r1Qi-rxYh.png) ### Lvalue & Rvalue * lvalue是程式運行時可以通過地址(address)存取的值(比如通過 & 來取得)(或者說==占用某記憶體空間==),意味著他們是變數或者可以被 dereferenced (通常用 *)的某一塊特定記憶體位置 * rvalue是暫時的結果,沒有實際佔記憶體空間 * ++ x 是左值,x ++ 是右值 * 所以 ++ (a ++) 會出錯 * 因為 a ++ 會回傳一個 rvalue(暫存值) * 又 ++ x 的 x 必須是個 lvalue,因為 ++ x 需要先寫回data, 需要有個位址(adderss)可寫入 * 可以把 lvalue 當作 rvalue 使用,反之則不行 ```c= int a = 1; /* a 是 lvalue */ int b = 2; /* b 是 lvalue */ int c = a + b; /* a+b 為 rvalue */ ``` ### typedef 參考 [c 語言 typedef 用法](https://magicjackting.pixnet.net/blog/post/65865174) ```c // 原始寫法: int *(*a[5])(int, char*); // 轉換1: typedef int *(*pFun)(int, char*); pFun a[5]; // 轉換2: typedef int *Func(int, char*); Func *a[5]; ``` ```c // 原始寫法: void (*b[10])(void (*)()); // 轉換為: typedef void (*pFunParam)(); // 右半部, 函數的參數 typedef void (*pFunx)(pFunParam); // 左半部的函數 pFunx b[10]; ```