---
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];
```