計算機系統中有四個內存區域:1)棧:在棧里面儲存一些我們定義的局部變量以及形參;2)字符常量區:主要是儲存一些字符常量;3)全局區:在全局區里儲存一些全局變量和靜態變量;4)堆:堆主要是通過動態分配的儲存空間,即以下講的講的動態分配內存空間。
定義int型指針int *p,p是用來儲存一個地址的值的,我們之所以要為p分配空間是讓它有一個明確的指向,比如你現在做好了一個指向方向的路標,但是你並沒有讓這個路標指向一個確切的方位,這個路標是瞎指的,這樣我們就不能夠通過它來明確到底哪里是東,哪里是西了。也就是說在計算機的內存里定義了一個指針變量,但是我們並沒有讓這個變量指示一個確切int類型變量的地址,所以必須要讓它有一個明確的指示方向。所以就要通過動態分配內存的方式來認為的規定確定其方向。
當malloc()函數為一個指針變量p分配了地址,當對p結束操作的時候要釋放p的內存空間。動態分配的變量時儲存在堆里面,但是這個堆的空間不是無限大的,也許當編一個小的程序的時候可能不會產生影響,但是對於那些大的程序,如果不及時釋放堆的空間就會發生內存泄露。所謂內存泄露是因為堆的空間被我們動態分配用完了,這樣再去使用動態分配堆的空間的時候就沒有足夠的空間可以使用了,這樣就需要占有原來的空間,也就是會把其他的空間來儲存我們鍵入的值,這樣會導致原來儲存的數據被沖掉,導致了內存泄露。
而且當使用malloc()函數釋放完空間,還要將原先的指針變量賦予一個NULL,也就是賦予一個空指針,留着下次的時候使用它。如果不賦予一個空指針這樣會導致原來的指針變量變成了一個野指針,就是一個沒有明確指向的指針,系統不知道它會指向什么地方,會是系統crash。
注:動態的分配內存。可以做到准確分配空間大小,不浪費資源,而且也不會發生程序不斷使用預先分配內存不足。動態分配的內存空間系統不負責自動回收,需要寫代碼手動釋放。
相對於malloc()函數,calloc()函數就不需要我們賦予NULL了,這是因為在每次調用完calloc()函數的時候系統會自動將原先的指針賦予一個空指針。除了malloc()、calloc(),還有realloc()函數,比前兩個函數分配更多的空間。
例子
1.指針的初始化
初始化不是指針的動態分配內存,但類似malloc函數,也就是讓指針變量有確定的方向或者有確定的地址指向。
1)定義並初始化
int a = 10; int*p = &a;
2)先定義再初始化
int a =10; int*p; p = &a;
實例:
1 #include <stdio.h> 2 3 int main(){ 4 int a = 10, b = 20, c = 30; 5 int *p = &a; //定義指針變量 6 *p = b; //通過指針變量修改內存上的數據 7 c = *p; //通過指針變量獲取內存上的數據 8 printf("%d, %d, %d, %d\n", a, b, c, *p); 9 return 0; 10 }
運行結果:
20,20,20,20
2.字符串指針
1)定義並賦值
char *pstr = "Hello World!";
2)先定義再賦值
1 char str[] = "Hello World!"; 2 char *pstr; 3 if(str) 4 { 5 *pstr = malloc(strlen(str)+1); 6 strncpy(pstr,str,strlen(str)+1); 7 } 8 else 9 { 10 pstr = NULL; 11 }
用完之后:
1 if(str) 2 { 3 free(pstr); 4 pstr = NULL; 5 }
3 指針作為函數形參實參
C語言中實參變量和形參變量之間的數據傳遞是單向的“值傳遞”方式。用指針變量作函數參數同樣要遵循這一規則。不可能通過執行調用函數來改變實參指針變量的值,但是可以改變實參指針變量所指向變量的值。
3.1 正確實例代碼,以輸入的兩個整數按大小順序輸出。正確的代碼如下:
1 # include<stdio.h> 2 3 int main(void) 4 { 5 void swap1(int * p1, int *p2); 6 int * pointer_1, * pointer_2; //定義兩個指針變量 7 int a, b; 8 printf("Please enter two number! \n"); 9 scanf("%d %d", &a, &b); 10 pointer_1 = &a; //將pointer_1指向變量a 11 pointer_2 = &b; //將pointer_2指向變量b 12 printf("a = %d, b = %d\n", a, b); 13 if (a<b) //判斷a和b的大小 14 swap1(pointer_1, pointer_2);//會改變指針指向的值 15 printf("max = %d, min = %d\n", a, b); 16 17 return 0; 18 } 19 20 void swap1(int * p1, int *p2) //定義swap函數,定義兩個指針變量形參p1, p2 21 { //形參和傳入實參的類型要一致 22 int temp; 23 temp = * p1; //改變指針變量所指向變量的值 24 *p1 = * p2; 25 *p2 = temp; 26 }
輸出結果:
1 Please enter two number! 2 a = 50, b = 60 3 max = 60, min = 50
3.2 錯誤代碼實例:
1 # include<stdio.h> 2 3 int main(void) 4 { 5 void swap2(int * p1, int * p2); 6 int * pointer_1, * pointer_2; 7 int a, b; 8 printf("Please enter two number!\n"); 9 scanf("%d %d", &a, &b); 10 pointer_1 = &a; 11 pointer_2 = &b; 12 printf("a = %d, b = %d\n", a, b); 13 if (a<b) 14 swap2(pointer_1, pointer_2); 15 printf("max = %d, min = %d\n", a, b); 16 17 return 0; 18 } 19 20 void swap2(int * p1, int * p2) 21 { 22 int * p; 23 p = p1; 24 p1 = p2; 25 p2 = p; 26 }
輸出結果:
1 Please enter two number! 2 a = 50, b = 60 3 max = 50, min = 60
代碼差異分析:
兩段代碼其實只是swap1和swap2函數的不同:
正確代碼傳入實參,指針變量p1和pointer_1都指向變量a,但改變的是實參指針變量所指向的地址的值(即a和b的值都已經改變),調用swap函數后,指針變量p1和p2都釋放掉。
錯誤代碼和正確代碼前面相同的,傳入實參,指針變量p1和pointer_1都指向變量a,但是改變的只是形參p1和p2的指向的地址,並沒有改變實參的任何東西,調用swap函數后,指針變量p1和p2會釋放掉,然后並沒有改變a和b的值。