Try   HackMD

指標篇 心得

例子:

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
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 這個變數的

#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 & 指標的指標

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
所以運用 指標的指標 改成

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可能執行錯誤
    ​​​​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
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  • int* const ptr2
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

Lvalue & Rvalue

  • lvalue是程式運行時可以通過地址(address)存取的值(比如通過 & 來取得)(或者說占用某記憶體空間),意味著他們是變數或者可以被 dereferenced (通常用 *)的某一塊特定記憶體位置
  • rvalue是暫時的結果,沒有實際佔記憶體空間
  • ++ x 是左值,x ++ 是右值
    • 所以 ++ (a ++) 會出錯
    • 因為 a ++ 會回傳一個 rvalue(暫存值)
    • 又 ++ x 的 x 必須是個 lvalue,因為 ++ x 需要先寫回data, 需要有個位址(adderss)可寫入
  • 可以把 lvalue 當作 rvalue 使用,反之則不行
int a = 1; /* a 是 lvalue */ int b = 2; /* b 是 lvalue */ int c = a + b; /* a+b 為 rvalue */

typedef

參考 c 語言 typedef 用法

// 原始寫法:
int *(*a[5])(int, char*);
// 轉換1:
typedef int *(*pFun)(int, char*);
pFun a[5];
// 轉換2:
typedef int *Func(int, char*);
Func *a[5];
// 原始寫法:
void (*b[10])(void (*)());
// 轉換為:
typedef void (*pFunParam)();       // 右半部, 函數的參數
typedef void (*pFunx)(pFunParam);  // 左半部的函數
pFunx b[10];