解讀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));}
...
推薦閱讀: