當前位置:才華齋>計算機>C語言>

C語言中的指標解讀

C語言 閱讀(2.49W)

C語言中的指標解讀

C語言中的指標解讀

指標無疑是一個難點。但是,我覺得指標也是C語言特別重要的一個特性。也許,你在除了C和C++以外的程式語言中,很少看到指標。而C++中,也多用引用,而非指標。指標,作為一種高效的工具,可謂是一把雙刃劍——用得好,可以大大提高程式效率,但用的不好,就是很多bug的滋生地。下面是指標相關的知識,歡迎閱讀。

這或許也是人們對指標褒貶不一的原因吧。就我個人而言,我還是很喜歡這個特性,因為我需要經常和硬體以及一些底層的軟體打交道。這個時候,指標便體現出它獨特的魅力。指標的知識很多,有一本經典的書叫《C和指標》,如果有興趣可以讀一讀。這裡,我主要總結一些如何去解讀指標(說實話這個東西實在是很容易讓人困惑)的方法,一方面給自己做查詢用,另一方面,希望可以給別人一些幫助。

  一,基本概念

關於指標的基本概念,我就不詳細介紹了,因為有許多書都介紹的很詳細。這裡我只介紹一部分。指標指向一個地址,而指標本身在大多數系統上都是一個無符號整數(在32bit機上是4byte,在64bit機上是8byte)。下面用一個例子來說明其機制:

在上面的例子中,先定義了一個指標p,它的型別是int,也就是說它只能指向一個int型的變數,而不能指向其他型別的變數。最後我們將a變數的地址賦給p。在這個過程中,涉及到兩個記憶體塊,一個是存放指標p的記憶體(用&p可得到記憶體地址),一個是存放a的值的記憶體塊(用&a可以得到記憶體地址)。而第一個記憶體存的p的值經過賦值語句後也就是&a的值了。另外一個注意點是, *(星號)和變數型別以及變數名之間可以有任意個空格,也可以沒有。比如下面三種方式都是一樣的:

int a = 10;

int *p; //宣告一個指標,但未初始化,此時為野指標

p = &a; //將a變數的地址賦給指標p

在上面的例子中,先定義了一個指標p,它的型別是int,也就是說它只能指向一個int型的變數,而不能指向其他型別的變數。最後我們將a變數的地址賦給p。在這個過程中,涉及到兩個記憶體塊,一個是存放指標p的記憶體(用&p可得到記憶體地址),一個是存放a的值的記憶體塊(用&a可以得到記憶體地址)。而第一個記憶體存的p的值經過賦值語句後也就是&a的值了。另外一個注意點是, *(星號)和變數型別以及變數名之間可以有任意個空格,也可以沒有。比如下面三種方式都是一樣的:

int* a;

int * a;

int *a;

解讀方法:

首先從標示符開始閱讀,然後往右讀,每遇到圓括號的右半邊就調轉閱讀方向。重複這個過程直到整個宣告解析完畢。需要注意的是,已經讀過的部分在後續作為一個整體來看。

看下面一個例子:

int *a[3];

//首先a右邊是[],說明a是一個具有3個元素的陣列

//右邊讀完,則讀左邊。a左邊是int*,說明a的元素是int型別的指標

int (*a)[3]

//首先,a右邊是圓括號的右半邊,轉向,左邊是一個*,說明a是一個指標

//遇到括號,再轉向,是一個[],說明a是一個指向3個元素的陣列的指標

//左邊是int,說明元素型別是int

//所以,a是一個指向具有3個整型元素的陣列的指標

int (*func)(int p);

//相同的方法,func首先是一個指標

//然後右邊是一個括號,說明(func)是個函式,而func是指向這個函式的指標

//這個函式具有int型別的引數,返回值型別為int

int (*func[3])(int p);

//同理,func首先是一個具有3個元素的陣列

//其次,func左邊是一個*,說明func陣列的元素是指標。要注意修飾的是func[3],而不是func。因為已經讀過的部分在後面都作為一個整體來對待

//跳出第一個圓括號,右邊又是一個圓括號,說明func陣列的元素是函式型別的指標。這個函式具有int型別的引數和int型返回值

  二,陣列首地址a,&a,&a[0]

注:a,&a,&a[0]的含義雖然不同,但是他們三個的.值是相等的!

以int a[3]為例說明:

a作為右值時,代表陣列首元素的首地址,而非陣列地址。 也就是a[0]的地址。int i = (a+1),這裡a是右值,所以代表首元素的首地址,a+1代表下一個元素的首地址,即&a[1]。

a是整個陣列的名字。所以sizeof(a)的值為sizeof(int) * 3 = 40,代表整個陣列的大小。

&a即為取a的首地址,也即整個陣列的首地址。所以sizeof(&a) = 4。 int p = (int)(&a+1)中的&a+1代表下一個陣列的首地址,顯然是越界的。

&a[0]代表首元素的首地址。 所以sizeof(&a[0]) = 4。

&a[3],很顯然陣列越界了,但它的sizeof是多少呢? 也是4,因為關鍵字sizeof求值是在編譯的時候,雖然並不存在a[3]這個元素,但是這裡並沒有真正訪問a[3],而是根據陣列元素型別來確定其值的。所以sizeof(a[3])不會出錯。

a[-1]代表什麼意思?首先要明白下標的形式被編譯器解析成指標的形式,即a[1]被解析成(a+1)。那麼,a[-1]被解析成*(a-1)。

關於陣列首元素的首地址和陣列的首地址的區別:其實,陣列首元素的首地址和陣列首地址的值是相同的,即&a[0]和a(以及&a)是相等的,但是而這含義不一樣。首元素的首地址加1後,是第二個元素的首地址(之所以一直說首地址,是因為有的型別儲存時會佔多個地址),但陣列的首地址加1後是“下一個陣列的地址”,這裡的下一個陣列只是為了說明加1時加了整個陣列的大小,而不是一個元素的大小。

有一點比較容易混淆:a雖然代表整個陣列,但(a+1)卻代表下一個元素的首地址,即和(&a[0]+1)一樣,下一個陣列的形式為:(&a+1)。 下面以一個程式來說明:

#include

int main()

{

int a[3] = {1, 2, 3};

printf("%ld",sizeof(long unsigned int));

printf("*(a+1)=%d",*(a+1));

printf("sizeof(a)=%ld", sizeof(a));

printf("sizeof(&a[3])=%ld", sizeof(&a[3]));

printf("a[-1]=%d*(a-1)=%d",a[-1],*(a-1));

printf("a=%p&a=%p&a[0]=%p",a, &a,&a[0]);

printf("a=%p(a+1)=%p(&a+1)=%p",a,(a+1),(&a+1));

return 0;

}

輸出結果:

8

*(a+1)=2

sizeof(a)=12

sizeof(&a[3])=8

a[-1]=0 *(a-1)=0

a=0x7fffcb4cb980 &a=0x7fffcb4cb980 &a[0]=0x7fffcb4cb980

a=0x7fffcb4cb980 (a+1)=0x7fffcb4cb984 (&a+1)=0x7fffcb4cb98c

說明(下面的行數只計算main函式內有程式碼的行):

程式第1行定義了一個具有3個元素的整型陣列。

第2行列印了long型的大小。因為我是64bit的,所以一個long是8byte。

第3行列印了*(a+1)的值,結果和a[1]的值相等。說明a雖然代表整個陣列,但作為右值時,的確代表首元素的首地址。

第4行輸出值為12,是整個陣列的大小。

第5行列印了一個出界元素的大小,沒有報錯,驗證了上面第5條。

第6行列印了a[-1]和*(a-1),輸出值相等。驗證了上面第6條。

第7行列印了a和&a[0],值相等。說明陣列的首地址和首元素的首地址是相等的。

第8行列印了a,(a+1),(&a+1),由結果就可以看出首元素的首地址加1是加了一個數組元素的大小,而陣列首地址加1是加了一個數組的大小。

三,指標陣列和陣列指標

指標陣列: 首先它是一個數組,陣列的元素是指標,也成為“儲存指標的陣列”。

陣列指標: 首先它是一個指標,它指向一個數組,也可以理解為“陣列的指標”。 也可以利用前面的“解讀方法”去分析。

四,函式指標和指標函式

函式指標: 指向函式的指標變數。

指標函式: 帶指標的函式,也就是返回指標的函式。

char * fun(char* a, char* b) //定義為指標函式

{

...

...

}

int main()

{

char* (*p)(char* p1, char* p2); //定義函式指標

p = &fun; //把函式地址賦給它

//p = fun; //這樣寫也行

(*p)("aa", "bb"); //使用函式指標

return 0;

}

四,指標常量和常量指標

const char* p1; //常量指標,指向常量的指標

char const* p2; //同上

char* const p3; //指標常量,指標是常量

怎麼記?

可以先把型別名去掉,然後看const離誰近,就修飾誰。

也可以const在*左邊的為常量指標,const在*右邊的為指標常量。

  五、萬x鑰匙

其實,關於“指標陣列與陣列指標、函式指標與指標函式、指標常量與常量指標”的判斷,有一個萬能x匙。那就是根據我們強大的中文語法:前邊是修飾詞,後邊才是主語。比如“指標陣列”,前面的指標只是修飾詞,後面的陣列才是主語,所以它是一個數組。

  六,野指標

野指標指沒有確定指向的指標。造成野指標的情況有:

1. 指標變數建立但沒有初始化。

2. 指標p被free或者之後,沒有置為NULL。