解讀C 語言指針

解讀C 語言指針

說到「指針」一定會聯繫到 C 語言。雖然 java/go/php/python 等 C 系語言都屬於 C 的衍生品,但「指針」只是在 C 語言中大行其道。也正時由於指針,使C 語言成為開發大型系統(操作系統、遊戲引擎等)的不二之選,可以說指針是 C 語言的靈魂。

同時,能不能靈活的使用指針,也是衡量 C 程序員的功能底的標準之一。

內存與指針

一個程序運行所需的所有數據都存儲在內存中,CPU 通過訪問內存獲取「指令信息」與「數據信息」,進而進行邏輯操作...

而記錄這些信息在內存中位置的變數即為「指針」。

內存地址與指針變數

  • 變數:程序中把代表著一定信息的符號稱為變數,如int a = 10
  • 內存地圵:信息在內存中的位置,如 0x7ffd4506b0e4
  • 變數與內存地址:程序運行時需要把 a=10 這個信息存儲在內存中,存儲的這個位置就是地址 &a
  • 指針變數:首先它是一個普通的符號 int *p = &a,不過它對應的內存中存儲的信息是一個地址 0x7ffd4506b0e4
  • 指針變數與內存地址:存儲指針變數的內存中的值是 0x7ffd4506b0e4,存儲的位置是 &p

指針變數與指針類型

指針變數 p 是一個值

如 0x7ffd4506b0e4,代表其在內存中的某個位置,*p 即為訪問這個位置

通過修改 p 的值,理論可以通過 *p訪問內存的任意位置

指針類型告訴程序應該怎麼理解這塊內存

  • int *p=0x7ffd4506b0e4, 代表內存在 0x7ffd4506b0e4 處存儲的值是一個 int
  • float *p=0x7ffd4506b0e4, 代表內存在 0x7ffd4506b0e4 處存儲的值是一個 float
  • struct XXX *p=0x7ffd4506b0e4, 代表內存在 0x7ffd4506b0e4 處存儲的值是一個 XXX 這個結構
  • 對於 int *p 在訪問 *p 時,程序在內存中取 sizeof int個位元組
  • 對於 struct XXX *p 程序在訪問內存時讀取 sizeof (sruct XXX) 個位元組
  • int * 解析思路 * int 我是一個指針,我指針向一個 int 結構

struct X{ int a; int b;}X 中的偏移 offset &((struct X*)(p)->b) - p技巧: offset = &((struct X*)(0)->b) 把 0 當作某 X 數據的指針,則 offset 處存儲的即為 bshort a = 0x1122;char c = *(char*)(&a)c == 0x22 // 小端存儲c == 0x11 // 大端存儲

函數指針

如上所屬,程序運行中所需的信息(函數體、數據)需要放在內存中。而指針的存在即為了定位內存中的這些數據。

用於定位數據的指針即為變數指針;用於定位數據的指針即為函數指針

void (*f)(int) = 0x7ffd4506b0e4: 代表內存在 0x7ffd4506b0e4 處存儲的是一個函數 (接受參數 int 返回類型 void)

void output(int a) { printf("%d
", a);}void (*f)(int) = &output; // 習慣性寫作: void (*f)(int) = output; f(10); // 即會輸出 10

一切皆是指針

因為一切都在內存里,因為一切都是內存里的數據(0/1),因為 CPU 正常工作依靠在內存中讀寫數據。

所以 C 語言中的所有符號都是指針

普通變數與指針變數

int a = 10; // 假設 &a 等於 0x7ffd4506b0e4a += 2

程序編譯結果 (涉及編譯鏈接原理)

  • 聲明 10 要放在 0x7ffd4506b0e4 內存處
  • a += 2 直接會編譯成 *(0x7ffd4506b0e4) += 2
  • 因為 int a,程序把 0x7ffd4506b0e4 的數據按 int 解析

int a = 10; // 假設 &a 等於 0x7ffd4506b0e4int *p = &a; // 假設 &p 等於 0x7ffd4506a111(*p) += 2

程序編譯結果

  • 聲明 10 要放在 0x7ffd4506b0e4 內存處

程序執行時

  • 0x7ffd4506b0e4 被存儲到 0x7ffd4506a111 位置
  • (*p) += 2 在 0x7ffd4506a111 讀取到 0x7ffd4506b0e4
  • *(0x7ffd4506b0e4) += 2 因為 int *p,程序把 0x7ffd4506b0e4 的數據按 int

指針語法糖

指針的指針

int a = 10;int *p1 = &aint **p2 = &p1 // 即 p2 存的是一個指針的地址

int ** 的解析方式即 ** int: 我是一個指針,我指針一個指針結構,這個「被指向的指針結構」指向一個 int 結構

數組指針與指針數組

char a[10];char *p = a; // 這個指針指向數組char *p[10]; // [10]*char 這是一個長為10的數組,裡面存了一堆指針,指針指向 char 結構

指針常量與常量指針

char achar * const p = &a; // const * char p 中一個常量指針,指針 charchar const *p = &a; // * const char p 是一個指針,指向 const char

  • 因為指針過於靈活,需要限制指針功能
  • char * const p p 不能再賦其它值
  • char const *p *p 不能再賦其它值

函數指針與返回指針的函數

char * func() // 返回指針的函數void (*f)(int); // 函數指針

返回指針的函數指針

char* (*f) (int);

返回函數指針的函數

int f1 (int i) { return i*2;}int (*f1()) (int){ return f2;}printf("%d", f1()(4));

返回函數指針的函數指針

typedef int (*t()) (int);int f1 (int i) { return i*2;}int (*f2()) (int) { return f1;}int main() { t *func_t = f2; printf("%d",(*func_t)()(2));}

...


推薦閱讀:

TAG:C編程語言 | C指針 | 編程語言 |