天天看點

C++指針專題(基礎掃盲)什麼是指針其他類型指針

指針的學習是每個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
           

看看這個圖可能會明白一點。

C++指針專題(基礎掃盲)什麼是指針其他類型指針

二級指針番外·

字元串指針數組篇

在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'
           

解釋一下上述代碼結果:

  1. 将 * * pstr分解成兩步:第一步 * 和 pstr 結合,指針解除一級引用,這時,* pstr是指向第一個字元串首位址的指針。

    第二步:* pstr 再和 * 結合,解除二級引用,此時的 * * pstr為第一個字元串首位址的解引用,即為 ‘h’ 。

  2. pstr++表示 指針 pstr 指向了 字元串指針數組的第二個元素:一個指向 “good bye” 的指針。
  3. 将*(* 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
           

過程可參考以下圖檔

C++指針專題(基礎掃盲)什麼是指針其他類型指針

有問題歡迎私信我,轉載請注明出處,謝謝~

繼續閱讀