1. 程式人生 > >理解C語言中指標的宣告以及複雜宣告的語法

理解C語言中指標的宣告以及複雜宣告的語法

昨天剛把《C程式設計語言》中“指標與陣列”章節讀完,終於把心中的疑惑徹底解開了。現在記錄下我對指標宣告的理解,順便說下如何在C語言中建立複雜宣告以及讀懂複雜宣告

本文章中的內容參考自《C程式設計語言》

指標是什麼就不詳細說明了,用一句話來總結就是:“指標是一種儲存變數地址的變數”。

1.宣告簡單的指標變數

先看看程式碼:

int i = 1;
int *p;     //宣告一個指向int型別資料的指標變數 p
p = &i;     //&為取地址符,把變數i的地址賦值給指標 p
*p = 2;     //此時 i 的值變成2了

這段程式碼聲明瞭一個指標變數p,並把它指向變數i,通過*p可以訪問到變數i,並對i的值進行修改。現在對星號 * 的作用進行詳細的講解。* 是一元運算子,它用在不同的地方將具有不同的作用。

1.1 星號 * 用於宣告語句時的作用

上面的程式碼片段中的第2行程式碼“int *p;”就是星號 * 用於宣告語句時的情況。對於指標的宣告,我們首先要從 p 這裡開始看起,這是C語言中“宣告”的語法,下面會介紹到。
第一步:先看p的右邊有沒有其它符號(分號不算),可以看到p的右邊並沒有符號。

第二步:看p的左邊,在p的左邊有一個星號 * ,關鍵的時候到了!在這裡可以看到星號 * 作用於p上面,其產生的效果是:“宣告變數 p 是一個指標 ”。目前為止,我們已經知道 p 是一個指標了,但還不知道該指標是指向什麼型別資料的。

第三步:看星號 * 右邊的型別說明符 int,在這裡int的作用就是宣告 p 指向的資料型別是int

。至此,“int *p;”這句程式碼就完全解釋完了。

注意!上面的 int 、* 這兩個符號發揮作用是有先後的,並不是組合在一起來發揮作用的。先是 * 發揮作用:宣告p是一個指標變數。然後是int發揮作用:宣告p指向的資料型別是整型資料

1.2 星號 * 用於宣告語句之外時作用

以上程式碼片段中,最後一句“ *p = 2; ”就是星號 * 用於宣告語句之外時的作用。這句語句等效於“ i = 2;”。
用專業點的說法就是:如果指標p指向整形變數 i,那麼在 i 出現的任何上下文中都可以使用 *p ,當 * 作用於指標變數p時,就是訪問指標變數p所指向的變數 i。

至此,已經說了如何宣告簡單的指標變數,是不是覺得很簡單呢?接下來將會按照複雜程度遞增的順序來介紹和指標有關的複雜宣告

2.複雜宣告和宣告語法

2.1宣告指向指標的指標變數

先看程式碼:

int i = 1;
int *p = &i;    //宣告指標變數p,p指向變數i
int **pp;       //宣告指標變數pp
pp = &p;        //pp指向變數p,p是指標變數
**pp = 2;       //此時 i 的值變成 2 了
//*(*pp) = 2;這個語句等效於上面的語句

程式碼段中的1、2行在上面已經介紹過了,現在主要介紹第2行之後的程式碼。
注意,這下面這兩句是等效的,這是因為類似於 * 和++這樣的一元運算子遵循從右至左的結合順序。

int **pp;   //宣告方式1
int *(*pp); //宣告方式2

現在開始解釋宣告方式2(和宣告方式1是一樣),同上面的簡單宣告一樣。我們再次從變數名pp開始。
第一步:先看看pp右邊有沒有符號,可以看到pp的右邊有一個右括號,而括號只是強調結合順序,所以不用管它。

第二步:看pp的左邊,可以看到,pp的左邊有一個星號 * (從右數起第一個星號),該星號的作用是:“宣告變數pp是一個指標。此時,我們還不知道指標pp所指向的資料型別,並且把括號裡面的 * 和 pp 兩個符號都解釋完了。現在看看括號外面的符號。

第三步:先看括號外面的右邊,可以看到,括號外面的右邊並沒有符號。

第四步:看括號左邊,這時,我們看到括號外面的左邊有一個星號 * (從右數起,第二個星號 *),這個星號 * 的作用是:“宣告指標變數pp所指向的資料型別是指標型別”。此時,我們已經知道了pp指向的資料型別是指標型別(即是程式碼片段中 p 的型別),但還不知道所指向的指標是指向什麼型別資料的。例如,pp指向指標p,但卻不知道p指向的是什麼型別的資料。

第五步:看最左邊的符號“int”,這個int的作用就是:宣告pp所指向的指標所指向的資料型別是整型int。例如,pp指向指標p,現在我們知道了p指向的資料型別是int整型。

哈哈,是不是有點暈了,現在簡單總結下:pp右邊第一個星號 * 聲明瞭pp是一個指標變數,第二個星號 * 聲明瞭指標變數pp所指向的資料型別是指標型別,而型別說明符int則聲明瞭pp所指向的指標指向的資料型別是int

看到這裡,你可能會疑惑:一定要按照上面的步驟來解釋宣告嗎?這是套路嗎?

答:是的。因為這是C語言中”宣告“的語法,下一小節將會對該語法進行介紹。之所以這麼遲才介紹這個語法,是因為在用過後,會更容易理解它。

2.2 C語言中”宣告“的語法

在這裡,將會對宣告的語法進行講解,為後面理解更加複雜的宣告打基礎。

在C語言中解釋一個宣告,要先從被宣告的變數名開始解釋。並不是從左到右,也不是從右到左解釋,而是從中間開始,更準確來講,是從被宣告的變數名開始解釋。而宣告總是由很多符號和唯一的變數名相結合,這些符號和唯一的變數名結合就是宣告符。宣告的形式為
”T D“的形式,其中 T代表型別,D代表宣告符。舉個例子吧:

int *p;

int就是T,而*p就是D。

下面將以變數名p來對宣告中用到的符號進行解釋。

變數名p可以和很多符號來結合成宣告符,如[ ],(),*。

1.當p與符號 [ ] 結合時(結合在p的右邊),符號 [ ]的作用就是宣告變數p是一個數組型別[ ]裡面的數字則決定了陣列中元素的個數。如下面的宣告程式碼:

int p[5];//宣告變數p是一個整型陣列,陣列中有5個元素

需要注意的是,符號[ ]的優先順序是比星號 * 的優先順序高的。

2.當p與符號 ( )結合時(結合在p的右邊),符號( )的作用就是:宣告p是一個函式,通過p()可以呼叫該函式,()中可以有引數列表或者無引數列表,如下面程式碼:

int p();//宣告一個函式p,返回值由前面的int決定
int p(int a,int b); //宣告一個帶引數的函式p

同樣,符號( )的優先順序比星號 * 高。

3.當p與星號 * 相結合時(結合在左邊),符號 * 的作用就是宣告p是一個指標型別 。如下程式碼 :

int *p; //宣告p是一個指標,該指標指向int型別資料

介紹完這三個符號後,可以繼續介紹語法(套路)了。

在解釋宣告時,首先要決定宣告的名字p是什麼東西,而和p最近的符號則決定了p是什麼東西。如下面宣告:

int p();    //p是函式
int *p;     //p是指標
int p[5];   //p是陣列
int *p[5];  //p是陣列,裡面的5個元素為指向int型別的指標

這裡的第4行程式碼要解釋一下,由於[ ]的優先順序比星號 * 高,所以[ ]先作用於p,故p是一個數組,現在我們知道p是一個數組了,但還不知道陣列中的元素是什麼型別,這時,就要看左邊的星號 * ,星號 * 聲明瞭陣列中的元素是指標型別。現在,我們知道了陣列中的元素是指標型別,但還不知道那些指標是指向什麼型別的,這時int發揮作用了,它聲明瞭裡面的指標是指向int型別資料的,所以這個宣告的結果就是:聲明瞭一個數組 ,陣列有5個元素,每個元素都是指向整型資料的指標

到這裡,我們對這個語法(套路)有點頭緒了!

原來解釋宣告就是要先從名字p開始,然後從p的右邊開始看符號(因為優先順序高的符號 [ ]和 ( ) 是放在右邊的),如果有符號,剛和p先結合。看完右邊的符號(如果有的話)後,就決定了p是什麼。這時,就到p左邊的符號發揮作用了(左邊要麼是 * ,要麼就什麼都沒有)。最後發揮作用是則是型別說明符(因為它在最外面)。

總的來說,解釋宣告的套路就是不斷問什麼,然後從裡往外看符號來解答什麼的過程,說過無謂,再來看一段程式碼:

int *p();   //宣告1,宣告一個函式p,返回型別為指標型別
int (*p)(); //宣告2,宣告一個函式指標p,p指向一個函式

我們來按照套路來解釋這兩個宣告
宣告1:首先,要確定p是什麼?從p右邊的符號 ( ) 可以解得,p是一個函式。新的問題又來了,那函式p的返回值是什麼型別?從p左邊的星號 * 可以解得,函式p的返回值型別是指標型別。新的問題又來了,返回的指標指向的資料型別是什麼啊?這時,按照套路,應該繼續看外一層的右邊(因為看符號的順序為p的右左右左…,而且內層優先於外層),然而符號 ( ) 的右邊沒有符號了,轉而看向外一層的左邊。這時發現外一層的左邊是型別說明符int,因此解得,返回的指標指向的資料型別是int。整個宣告就是:宣告p為一個函式,函式的引數為空,函式的返回值為指標,指標指向的資料型別為int。

宣告2:由於這個宣告中,有個括號把星號 * 和 p包住了,所以 * 和 p屬於內層,而括號中p的右邊沒有符號,故 * 和p先結合,則聲明瞭p是一個指標,這回答了p是什麼。新的問題來了,p指向的是什麼?這時,內一層的符號看完了,繼續看外一層的右邊,外一層的右邊是符號( ) ,則解得p指向的是一個函式。然後再看外一層的左邊得,該函式的返回值是int型別。整個宣告就是:宣告p是一個指標,這個指標指向一個函式,這個函式的返回值為int

總結一下,解釋宣告的套路就是:先從最內層開始看符號,先從名字p的右邊開始看,再到左邊。然後跳到外一層的右邊開始,再到外一層的左邊開始看。不斷迴圈,直到沒符號為止

下面把這個套路應用到更加複雜的宣告中去。

如下面這個宣告:

char (*(*x())[])();

有沒有頭暈的感覺?不用怕,按照套路來很簡單的。
首先從最裡面開始,x的右邊是符號 ( ) ,所以x是一個函式。再看x的左邊,x的左邊是一個星號 * ,所以 函式x的返回值型別為指標型別。繼續跳到外一層的右邊,發現外一層的右邊是符號 [ ] ,所以指標指向的是一個數組。繼續看左邊,發現左邊是一個星號 * ,所以陣列中的元素的型別為指標型別。再看外一層的右邊,發現符號 ( ),所以陣列中的指標指向的是函式。最後看左邊的型別說明符char,所以函式的返回值型別為char型別。整個宣告就是:x是一個函式,函式的返回值型別是指標型別,這個指標指向的是一個數組,這個陣列是指標陣列(裡面的元素是指標),陣列中的指標是函式指標(指標指向函式),指標指向的函式的返回值型別是char型別。

再看這個宣告:

char (*(*x[3])())[5];

該宣告的結果為:x是一個數組,陣列有3個元素,元素的型別為指標型別,這些指標都是函式指標,所指向的函式的返回值為指標 ,返回的指標指向陣列,指向的陣列有5個元素,元素的型別為char型別。

到此為止,我們已經學會了複雜宣告的語法(套路)。這時我們也可以利用這個語法來宣告我們想要的變數。

2.3 使用複雜宣告

現在我們來宣告一些複雜的宣告。

比如,我們來宣告一個函式x,函式x的返回值為指標型別,該指標指向一個數組,陣列的元素型別為char
只要按套路反過來就行了:
1. 函式x:

x()
  1. 函式x的返回值為指標型別: 。
*x()

3.該指標指向一個數組:

(*x())[]

4.陣列的元素型別為char:

char (*x())[];

搞定!是不是很簡單?

這篇文章到這裡就結束了。這是我的第一篇部落格,如果有什麼錯誤,請在評論中回覆。希望這篇文章能幫助到大家更好地理解C語言中的宣告的語法。

相關推薦

理解C語言中指標的宣告以及複雜宣告語法

昨天剛把《C程式設計語言》中“指標與陣列”章節讀完,終於把心中的疑惑徹底解開了。現在記錄下我對指標宣告的理解,順便說下如何在C語言中建立複雜宣告以及讀懂複雜宣告。 本文章中的內容參考自《C程式設計語言》 指標是什麼就不詳細說明了,用一句話來總結就是:“指標是

C語言指標指標陣列的理解

指標就是地址! 概念 C語言中,允許用一個變數來存放地址,這種變數稱為指標變數。一個指標變數的值就是某個記憶體單元的地址。指標變數就是用來存放指標(地址)的變數! &運算子:求某一變數在儲存單元中的記憶體地址。 *運算子:取出指標變數所指向變數的內容,後面

深入理解C語言兩級指標(char **pptr)的引數的用法

最近在看亞嵌的《Linux C程式設計一站式學習》,對其中的兩層指標的引數用法有些疑惑,下面和大家分享一下學習心得! 首先來看一段程式碼: /* main.c */ #include <stdio.h> #include <stdlib.h>

深入理解C語言指標與陣列之指標

前言          其實很早就想要寫一篇關於指標和陣列的文章,畢竟可以認為這是C語言的根本所在。相信,任意一家公司如果想要考察一個人對C語言的理解,指標和陣列絕對是必考的一部分。          但是之前一方面之前一直在忙各種事情,一直沒有時間靜下心來寫這些東西,畢竟

理解C#語言的類型轉換----初學者的理解,請大神指教

寫代碼 需要 con c# 初學 har 3.1 parse 範圍 一下都是在視頻教學中學到後的理解,如果說錯了請大神指教 C#語言中的類型轉換,就是將某個數據要轉換成另一個類型的數據。 c#語言中的數據類型主要有: char類型(字符類型); string類型(字符串類型

c語言指標陣列和陣列指標的認識

1.陣列:一批具有同名的同屬性的資料就組成了一個數組。(“[ ]”也是陣列型別的一部分)     由此可知:(1)陣列是一組有序資料的集合;(2)陣列中的每一個元素都屬於同一個資料型別。  定義一維陣列的一般形式:型別符  陣列名【常量表達式】(陣列也

如何理解 C 語言的 typedef

作者:Skycell 連結:https://www.zhihu.com/question/19894694/answer/13278588 先熟悉函式指標的宣告形式:資料型別 (* 指標變數名) (形參列表);  函式指標—指向函式的指標 

C語言指標的值賦值給陣列

  如果把各種語言做個冷兵器類比的話,C語言一定是刀客的最佳工具.入門很簡單,但是要是能把它熟練運用,那就是頂尖級別的高手了. 用了那麼多年的C語言,發現自己還是僅僅處於熟練的操作工.今天遇到了一個bug,就是和指標的賦值有關係.請看程式碼: 1 #include <stdio.h>

C語言指標陣列的記憶體地址

char arr[3]; printf("arr:\n%d\n%d\n%d\n", arr, arr + 1, arr + 2); char *parr[3]; printf("parr:\n%d\n%d\n%d\n", parr, parr + 1, parr + 2);

C語言變數和函式的宣告與定義

一、變數在將變數前,先解釋一下宣告和定義這兩個概念。宣告一個變數意味著向編譯器描述變數的型別,但並不為變數分配儲存空間。定義一個變數意味著在宣告變數的同時還要為變數分配儲存空間。在定義一個變數的同時還可以對變數進行初始化。 區域性變數通常只定義不宣告,而全域性變數多在原始檔中定義,在標頭檔案中宣告。 區域性變

C語言指標變數所佔位元組大小

在學習過程中知道,一個任何型別的指標變數所佔的位元組大小都為4個位元組。這是為什麼呢? 記憶體是由位元組組成的,每個位元組都有一個編號。指標變數主要是存放相同資料型別的變數的首地址。這裡的這個地址其實就是記憶體的某個位元組的編號。而這個編號的確定是與地址匯流排有關。如果地址

C語言指標變數作為函式引數和一般變數作為函式引數的區別

函式的引數不僅可以是整型、浮點型、字元型等資料,還可以是指標型別。它的作用是將一個變數的地址傳送到另一個函式中。 指標變數作為函式引數和一般變數作為函式引數是有區別的,對於這種區別初學者一般都很迷惑。下面我將就一個簡單的例子來說明一下它們的區別。看透以後也許也就不那麼疑惑了。

C語言指標初始化和常規運算

1. 指標初始化注意須知 <1>指標變數和普通變數一樣,外部或者靜態指標變數若未初始化,則被自動初始化為NULL,它的值為0(ASCII字元NULL的程式碼)。 <2>可以

深入理解C語言函式指標

我們一開始只是從功能上或者說從數學意義上理解myFun這個函式,知道myFun函式名代表的是一個功能(或是說一段程式碼)。函式名到底又是什麼東西呢?函式指標變數     一個數據變數的記憶體地址可以儲存在相應的指標變數中,函式的首地址也以儲存在某個函式指標變數中。這樣,我就可以通過這個函式指標變數來呼叫所指向

C語言指標傳遞問題

要求:通過呼叫函式實現兩個值的交換 例如:輸入5,9 —– 輸出9,5 不能到達預期的結果的程式碼 #include<stdio.h> void main() { void swap(int *p1, int *p2)

C語言結構體以及在結構體呼叫方法;聯合體和聯合體的使用

#include <stdio.h>  #include<stdlib.h>void study(){}; struct student{ int age; int number; void(*study1)(); } main(){//使用,在結構

c語言指標加一之後

研究下指標p+1之後,話不多說貼程式碼測試 #include<stdio.h> struct test { int a; int b; char c;

正確理解C語言指標的 &a+1,假設a為一個數組

1.int a[5]={1,2,3,4,5}; int p=(int)(&a+1); printf("%d",*(p-1)); 答案為什麼是5? 這個問題的關鍵是理解 &a a是一個數組名,也就是陣列的首地址。 對a進行取地址運算子,得到的是一個指向陣列

簡析c語言結構體的宣告使用以及位段

1.結構體的定義 聚合資料型別能夠同時儲存超過一個的單獨資料。c語言中提供了兩種型別的聚合資料型別,陣列和結構。結構也是一些值的集合,這些值稱為它的成員。但每個成員可能具有不同的資料型別。結構體變數屬於標量型別,所以你可以像對待其他標量型別那樣執行相同型別的操作。結構體也可

關於C語言的陣列指標指標陣列以及二級指標

概念解釋 陣列指標:首先它是一個指標,它指向一個數組,即指向陣列的指標;在32 位系統下永遠是佔4 個位元組,至於它指向的陣列佔多少位元組,不知道。陣列指標指向的是陣列中的一個具體元素,而不是整個陣列,所以陣列指標的型別和陣列元素的型別有關。 指標陣列:首先