什么是指針:
指針是一種特護的數據類型,使用它可以定義指針變量,指針變量存儲的是整型數據,代表內存的編號,通過這個編號可以訪問到對應內存。
為什么使用指針
1、函數與函數之間是相互獨立的,但是有些時候需要共享變量傳參是單向值傳遞全局變量容易命名沖突使用數組還需要傳遞長度函數的命名空間是相互獨立的,但是地址空間是同一個,所以指針可以解決這個問題
2、由于函數傳參是值傳遞(內存拷貝),對于字節數較多的變量,值傳遞的效率較低,如果傳遞的是變量的地址,只需要傳遞4|8字節,根據32|64位決定
3、堆內存無法取名,它不像stack、bss、data讓變量名與內存建立聯系,只能使用指針來記錄堆內存的地址編號從而使用該堆內存。
如何使用指針:
定義: 類型* 變量名_p; int* p;1、由于指針變量與普通變量的用法上有很大的區別,為了以示區分,建議在變量名后面加上_p2、指針的類型表示該指針變量存儲的是什么類型變量的地址,指針的類型決定了可以通過指針變量訪問的字節數3、一個*只能定義一個指針變量int a1,a2,a3;int* p1,p2,p3;//p1指針,p2,p3是intint *p1,*p2,*p3;//p1,p2,p3都是指針變量4、指針變量與普通變量一樣的是隨機的默認值,一般初始化為NULL(空值)。賦值(引用):變量名_p = 地址;//地址必須是有權限而且有意義的內存地址指向棧內存:int num;int* p = NULL;p = #指向堆內存:int* p = NULL;p = malloc(4);解引用: *p通過指針變量中記錄的內存編號來訪問對應的內存,該過程可能產生段錯誤。但是原因是因為存儲了非法的內存編號注意:訪問的字節數是由指針變量的類型決定的。*p=1000;
練習1:實現一個變量交換函數,調用它對一個數組進行排序
#include<stdio.h>void swap(int* p1,int* p2)
{int change=0;change=*p1;*p1=*p2;*p2=change;
}int main(int argc,const char* argv[])
{int num[10]={8,0,5,6,9,3,2,7,4,1};for(int i=0;i<9;i++){for(int j=i+1;j<10;j++){if(num[i]<=num[j])swap(&num[i],&num[j]);}}for(int i=0;i<10;i++){printf("%d",num[i]);}}
練習2:實現一個函數,計算兩個整數的最大公約數、最小公倍數,最大公約數用return返回,最小公倍數使用指針返回
注意:想要獲取多個返回值,可以借助指針返回
#include<stdio.h>int big(int n1,int n2)
{int a=n1>n2?n1:n2,b=n1>n2?n2:n1,c=1;while(c){c=a%b;a=b;b=c;}return a;
}int small(int n1,int n2)
{int a=n1>n2?n1:n2,b=n1>n2?n2:n1;int i=1;while(a*i%b!=0){i++;}return a*i;
}int max_min(int n1,int n2,int* p)
{*p=small(n1,n2);return big(n1,n2);
}int main(int argc,const char* argv[])
{int max=0,min=0;int num1,num2;printf("請輸入兩個數:");scanf("%d%d",&num1,&num2);max=max_min(num1,num2,&min);printf("%d %d\n",max,min);
}
使用指針需要注意的問題:
空指針:值是NULL的指針變量都是空指針,如果對空指針進行解引用產生段錯誤。一般NULL也是一種錯誤標志,如果一個函數返回值是指針類型時,當函數執行出錯可以返回NULL表示該函數執行出錯。注意:NULL在絕大多數的系統中都是0,在個別系統上是1if(NULL==p)if(!p)如何避免空指針帶來的段錯誤:使用來歷不明的指針之前先做判斷是不是空指針。1、當函數的參數是指針時,別人傳給你的就有可能使空指針2、從函數獲取返回值使,也可能獲取到的使空指針野指針:指針指向不確定的內存的指針野指針解引用的危害:1、臟數據2、段錯誤3、一切正常野指針的危害比空指針更嚴重,因為野指針無法判斷出來,而且可能是隱藏性的錯誤,短時間內不暴露所有的野指針都是人為制造的,如和避免產生野指針:1、定義指針是一定要初始化2、函數不返回局部變量地址3、指針指向的內存被釋放后,指針變量立即置空=NULL
指針的運算:
指針變量里存儲的是整型,理論上整型數據能使用的運算符她都能用,但絕大多數都是無意義的。指針+n:指針+指針類型的寬度*n(int就是 4) 前進了n個元素
指針-n:指針-指針類型的寬度*n 后退了n個元素
指針-指針:(指針-指針)/指針類型寬度 計算相隔了多少個這種元素
指針與const:
const int* p;
當我們為了提高傳參效率而使用指針時,傳參的效率雖然提高了,但是變量也有被修改的風險,這種寫法就可以保護指針變量指向的內存不被修改int const *p; 效果同上
int* const p; 保護指針變量不被修改const int* const p; 保護指針變量和指針變量指向的內存不被修改
int const * const p; 效果同上const保護誰按照就近原則
指針數組與數組指針:
指針數組:由指針組成的數組,它的成員都是指針變量類型* arr[長度];數組指針:專門指向數組的指針類型 (*arr)[長度];
指針與數組名:
數組名是一種特殊的指針,它是常量,不能改變它的值,它與數組內存是映射關系,沒有自己的內存空間
數組名 == &數組名 == &數組名[0]指針變量它由自己的存儲空間,如果它存儲的是數組的首地址,指針可以當作數組使用數組名[i] == *(數組名+i)*(指針名+i) == 指針名[i]注意:數組作為函數的參數時蛻變成了指針,所以長度丟失了。
二級指針:
二級指針就是指向指針的指針。里面存儲是指針變量的地址。
定義:類型** 變量名_pp;
賦值:變量名_pp = 地址;變量名_pp = &指針變量;
解引用:*變量名_pp <=> 指針變量 p**變量名_pp <=> *指針變量 *p注意:當函數之間需要共享指針變量是必須傳遞二級指針
函數指針:
函數返回值 (*函數指針名p)(參數列表);函數名就是一個地址(整數),它代表了函數在代碼段中的位置可以通過函數指針,把函數作為參數傳遞給另一個函數,這叫回調函數指針就是指向函數的指針,里面存儲的就是該函數在代碼段中的位置。