在研究JM8.6中內存分配模塊(memalloc.c)時,看到以下代碼:算法
若是C語言基礎較好的話,對上面也比較好理解。函數
可是再看到下面:工具
以及再下面:佈局
可真得花點時間去琢磨內存如何佈局的,以及如何訪問到指望位置的值。spa
本篇文章就準備將背景知識和上面的三個內存分配函數,介紹一下,以供有此困惑的人來理解。3d
其實,很早以前就有該想法,針對C語言的指針來作一次科普,但因爲拖延症的緣故一直沒動筆。指針
本篇是基礎篇,後面會再開一篇來介紹上面三個函數的原理及內存佈局。code
1. 什麼是指針?blog
所謂指針,就是某個地址空間,存儲着一個值(指針值),這個值爲某個內存地址。內存
以下圖:(請原諒我拙劣的畫圖水平,拿win10自帶的繪圖工具畫的。。。)
拿32位系統來進行說明,每一個指針(無論什麼類型的指針——內置類型或自定義類型)的sizeof都爲4Bytes,而且爲
了高效訪問,通常都是4字節對齊的(存儲ptr值的這段內存地址addr2,打印其值,最後兩個二進制位爲0)。
上圖中,內存空間addr2中存儲着一個指針值——ptr(ptr的值爲addr1),即這個ptr指針指向addr1這個地址空間。
2. 棧指針 & 堆指針
計算機系統中兩種內存類型——stack和heap,stack爲棧內存,如函數內臨時變量、函數參數,heap爲堆內存,如malloc
分配的空間,其從系統中獲取,通常分配和回收使用夥伴算法(buddy)。
其中stack增加方向向下,heap增加方向向上,以下demo及打印:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 void stack_test() 5 { 6 int tmp0; 7 int tmp1; 8 int tmp2; 9 printf("stack_addr: &tmp0=%p, &tmp1=%p, &tmp2=%p\n", &tmp0, &tmp1, &tmp2); 10 } 11 12 void heap_test() 13 { 14 char *ptr0 = (char*)malloc(64); 15 char *ptr1 = (char*)malloc(64); 16 char *ptr2 = (char*)malloc(64); 17 printf("heap_addr: ptr0=%p, ptr1=%p, ptr2=%p\n", ptr0, ptr1, ptr2); 18 } 19 20 int main(void) 21 { 22 stack_test(); 23 heap_test(); 24 }
從運行結果看,tmp0~2這種stack變量的地址,從高地址往低地址變化,而ptr0~2這種指向heap空間的值,從低往高變化。
然而,ptr0~2做爲stack類型變量,其地址仍符合stack的增加方向(從高往低變化)。不信能夠打印出&ptr0, &ptr1, &ptr2的值。
3. 如何給一個指針變量賦值?
使用以下方式:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void mem_alloc(char **pp) 5 { 6 *pp = (char*)malloc(64); 7 } 8
9 int main() 10 { 11 char *ptr0; 12 char *ptr1; 13 char *ptr2; 14
15 char ch = 'A'; 16 ptr0 = &ch; //ptr0指向stack變量ch
17 ptr1 = (char*)&ptr2; //ptr1指向ptr2這個stack變量的地址
18 mem_alloc(&ptr2); //給ptr2這個stack變量賦值,賦值爲heap空間地址
19 printf("stack_addr: &ptr0=%p, &ptr1=%p, &ptr2=%p, &ch=%p\n", &ptr0, &ptr1, &ptr2, &ch); 20 printf("ptr_val: ptr0=%p, ptr1=%p, ptr2=%p\n", ptr0, ptr1, ptr2); 21 }
須要注意一點的是,若是經過函數調用給一個變量初始化,那麼參數必須是該變量的地址,如18行的:mem_alloc(&ptr2);
爲何?
傳地址才能修改該地址處的值(ptr2這個stack變量的內存地址空間中,所保存的值——指向哪兒),而傳值只是進行了一份數據拷貝,
等調用的函數退出後,原先被拷貝的變量什麼都沒改變。
所以,若是想修改15行中ch這個stack變量的值,就傳其地址:&ch;若是想修改13行的ptr2這個stack變量的值,也傳其地址:&ptr2,
正如18行中所調用的。
4. 指針、雙指針、三指針、四指針
指針也能夠稱爲單指針,相似於這種:void* ptr; 如上面代碼中的ptr0,ptr1,ptr2。
雙指針爲指向指針的指針,相似於:void** pptr; 如第4行中的形參:void mem_alloc(char **pp)。
三指針其實又多了一個*,形如:void*** ppptr; 能夠理解爲一個指針指向一個雙指針,而該三指針變量值(該變量內存空間中的值)爲第一個指針值。
四指針也又多了一個*,形如:void**** pppptr; 再多一道中轉。
畫個圖來表達:
其中,存儲字符的空間我故意畫小,由於其大小爲1Byte,而指針的大小爲4Bytes。