指針的學習是每個C/C++初學者都無可避免的,最近在學習C++的同時,順便複習了一下以前學的C的指針,有些地方本來不是很明白的現在也豁然開朗了(⊙o⊙)
什麼是指針
讓我們看看維基百科上的定義:計算機中的記憶體都是編址的,每個位址都有一個符号,就像家庭位址或者IP位址一樣。指針,是一個無符号整數(unsigned int),它是一個以目前系統尋址範圍為取值範圍的整數。聲明指針和聲明一個無符号整數實質上并無差別。
現在,我們從四個方面來考慮這句話:
1.指針的位址
首先我們回憶一下我們是建立定義程式中的變量的:
int a;//整型變量
char ch;//字元型變量
當我們建立完成後,記憶體就會為這些變量配置設定一個取決于變量大小的空間,這些變量就存儲在這些空間上,且每個空間都有獨一無二的位址,就像自家門牌号一樣。同理,我們在程式中定義一個指針變量方式也是一樣的:
int *p;
char *pchar;
這裡的符号 * 是聲明指針特有的,用來表示這個變量它是一個”指針“。那麼,既然是我們自己建立的變量,那它也同樣有資格在記憶體上占有一席之地。是以,每個指針變量也有自己的家門牌号。
2.指針的值
重點來了,指針之是以為指針,正是因為它的值與衆不同。回憶以前學習過的變量類型:int,char,int[ ],double,float等等,它們的值是一個确切的值,而指針的值,是一串位址,這個位址是記憶體空間上已有的,屬于另一個變量的位址。說白了,就是别人家的門牌号,逃)是以,我們也可以說指針”指向“了另一個變量。
3.指針初始化
當一個指針變量被建立時,它并沒有指向某一變量,或者說沒有指向我們所“期望”的變量。為什麼這麼說?因為我們知道所有未初始化的變量它的輸出是一個很大的”垃圾數“,而這個”垃圾數“很可能湊巧就是某一變量的位址,是以未初始化的指針可能就指向了該變量,而我們對此卻是無進而知的。是以,我們需要養成良好的習慣,即建立指針的同時為其初始化,以免發生記憶體安全問題。
指針初始化可如下:
int *p=NULL;
int a;
int *q=&a;
4.指針類型
指針的變量必須是和指向的變量保持一緻性。比如指向int型變量,那麼指針類型就必須是int *,其他類型同理。
其他類型指針
數組指針
運算符優先級:
()>[ ]> *
首先我們應先明确,我們以前建立的數組如int a[10];,其名字a就是一個指針常量。
顧名思義,數組指針就是一個指向數組的指針,其建立文法如下:
int a[3]={1,2,3};
int (*p)[3]=&a;
上述代碼中,我們建立了一個int( *)[ ]類型的指針,那麼理所當然,它指向的變量類型一定是int [ ],隻要去掉 * 即可。我們将數組a的位址傳遞給指針p,此時p就指向了該數組。
此後,我們可以對該指針進行操作:
printf("%p\n",p);//數組a的位址
printf("%p\n",&a);//數組a的位址
printf("%p\n", a);//數組a的首元素位址
printf("%p\n",*p);//數組a的首元素位址
以上四個輸出都是一樣的,都是數組a的位址。
若想得到a[1]的值,隻需執行以下語句:
因為在數組指針中,(*p)等價于a
指針數組
顧名思義,指針數組本身就是一個我們熟悉的數組,隻不過其數組元素全是指針。
int* p[2];//包含兩個整型指針的數組
int a=10,b=20;
p[0]=&a;
p[1]=&b;
printf("%d %d\n",*p[0],*p[1]);//輸出為10,20
以上代碼中,我們聲明了 int * 類型的數組,其每個元素的類型都是int * ,分别指向整型變量a和b。
二級指針
二級指針就是指針的指針,它所指向的變量是另一個指針,是以二級指針的值存儲的是另一指針的位址,而另一指針存儲的是它所指向的變量的位址。
int a=5;
int* p=&a;
int** pp=&a;
printf("%d %d\n",*p,**p);//答案都為5
看看這個圖可能會明白一點。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICMx8FdsYkRGZkRG9lcvx2bjxSNx8VZ6l2cs0TPR90drpWT1kleNBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyczMxIDNxMjMwEzNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
二級指針番外·
字元串指針數組篇
在C語言中,我們通過建立一個字元數組來存儲一個以 ’\0’ 結尾的字元串,同樣我們也可以用指針來建立一個字元串,并且對它進行指派:
const char *ps = "hello world";
char s[20] = "hello world";
printf("%s %s\n", ps, s);//結果都為hello world
ps = "good bye";//正确
s = "good bye";//錯誤,無法編譯
為什麼字元指針 ps 可以直接用 = 号指派而數組 s 不可以呢?還是那句話,指針存儲的是一個位址,在這裡指針ps存儲的就是字元串 ”hello world“ 的首字元’ h’ 的位址,在後面把 "good bye"指派給ps的時候,“hello world” 字元串仍然在記憶體空間中存在,隻不過是指針 ps 改變了存儲的位址,指向了字元串 “good bye”。
const char * ps = "hello world";
printf("%p\n", ps);//在我的PC上是0x00E19B98
ps = "good bye";
printf("%p\n", ps);//在我的PC上是0x00E19E08,和上面不同
言歸正傳,開始建立一個字元串指針數組并初始化。
const char * ps[2] = { "hello world","good bye" };
printf("%s\n", ps[0]);//ps[0]存儲的是第一個字元串的首字元'h'的位址,其輸出是整個字元串
printf("%c\n", *(ps[0]+1));//對'h'的位址解除引用那麼就是'h'這個字元
然後,我們用一個二級指針來指向這個字元串指針數組。
const char **pstr = ps;
printf("%c\n", **pstr);//輸出為'h'
pstr++;
printf("%c\n", *(*pstr+3));//輸出為第二個字元串中的'd'
解釋一下上述代碼結果:
-
将 * * pstr分解成兩步:第一步 * 和 pstr 結合,指針解除一級引用,這時,* pstr是指向第一個字元串首位址的指針。
第二步:* pstr 再和 * 結合,解除二級引用,此時的 * * pstr為第一個字元串首位址的解引用,即為 ‘h’ 。
- pstr++表示 指針 pstr 指向了 字元串指針數組的第二個元素:一個指向 “good bye” 的指針。
-
将*(* pstr+3)分解成三步:第一步 * 和 pstr 結合,指針解除一級引用,這時 * pstr 是指向第二個字元串第一個字元位址的指針。
第二步: * pstr +3 ,即将指針 * pstr在第二個字元串上的位置向後移動三個機關,此時,* pstr 是指向第二個字元串第四個字元位址的指針。
第三步 : * (*pstr +3),就是将指針( * pstr +3)解除引用,原來它指向的是字元 ’d’ 的位址,那麼解除引用後就是字元 ‘d’ 。
可以參考以下圖檔:
如果還有疑惑,沒關系,再舉一個例子:C++指針專題(基礎掃盲)什麼是指針其他類型指針
int a = 10, b = 20;
int *p[2] = { &a,&b };//建立了一個整型指針數組,用a和b的位址來初始化
int **pp = p;//建立了一個指向整型指針數組的二級指針
printf("%d %d\n", *p[0], **pp);//輸出都是10
printf("%d %d\n", *p[1], *(*(pp + 1)));//輸出都是20
過程可參考以下圖檔
有問題歡迎私信我,轉載請注明出處,謝謝~