C語言中申明解析方法的總結

2021-03-03 22:32:51 字數 4918 閱讀 9608

2011-11-04 00:00位元網悠虎

關鍵字:c語言

c語言中的宣告是乙個比較令人頭痛的問題,尤其一些複雜的宣告看起來甚至會令人感到恐怖,如果您是一位初學者,您一定會對下面這幾個式子感到畏懼。因為對於剛接觸這種形式的人來說,這簡直太複雜了,當然前提是假定您未掌握分析這方面的專業知識。

第乙個宣告: void(*signal(int sig, void (*func)(int)))(int);第二個宣告:const char *const(*sinc(char *sincg(),int (*p)(int a,int *b)))(int **sing);其中第乙個宣告是某系統函式的宣告,而第二個宣告是我自己即興創作的,但我敢保證它除了複雜一點之外沒任何缺點。

如果你對這兩個宣告感到異常頭痛,並且急迫的想知道如何分析這樣的宣告來提高自己的c水平和熟練度。那麼就請閱讀下面的內容,讓我將自己所學到的知識與您一起分享吧!

首先我們來看乙個很簡單的例子——複雜的東西都是由簡單的構成,一旦將所有簡單的都搞明白了,你才能,並且也可以輕易的弄懂複雜的問題,而那時候便只需要某乙個轉機就行了,那轉機往往就是總結。

char next ;大家都很容易知道,這裡宣告了乙個char 型別的變數next.這裡的next是乙個識別符號,表明它的變數身份,所以我們可以這樣想,一旦碰到識別符號,如同next,就用這樣一句話替代,(識別符號)是………,比如前面的next,可以用next是……來替代,如同c語言中的巨集定義一樣,如果這樣的說法讓你感到有點暈,我們換一種方式,乾脆這樣定義一下,#define 識別符號識別符號是……

也就是說,一旦碰到識別符號,你腦海中就立刻用(識別符號)是………這樣一句話來完全替代那個識別符號。於是,剛才的那個宣告,char next;我們用更規範的類似數學上的形式思維來考慮一下,首先我們分析識別符號next,根據前面的說法,我們看到next之後,立刻用next是…。來替代,這樣,我們就得到了整個宣告的前一部分:

「next是……。」這樣乙個句子,但是next究竟是什麼呢,這時候我們需要看宣告中的其他內容,比如上例中的char,這時候我們很自然的就弄清楚了next是(乙個char型別的變數)。所以char表明的是乙個型別變數。

於是,經過這樣乙個思維過程,上面的宣告就分析完成了。

大家先不要失去耐心,我用這麼長的篇幅來敘述這樣乙個簡單的例子並不是掉你胃口,相反,是為了讓你更加容易理解下面的內容。使我的文字跳躍度不至於讓你的思維感到突兀甚至短路——這不是我寫這篇總結的目的。

接下來我們把這個宣告稍微加長一點,使它變成char next();或者是char next;這時候,根據我們所學的知識,依然能夠很輕易的看出,前面乙個是宣告的返回char型別變數的函式,而後乙個是宣告乙個char型別變數的陣列,正是這個時候,有乙個問題請你注意,我們新增這個括號之後究竟使我們的思維改變了什麼,為什麼當這兩個符號出現之後我們裡就就明白了這是乙個函式或是乙個陣列,所以,在這裡我想告訴你也許你從未意識到過的乙個新知識點就是,圓括號和方括號是宣告中的最高優先順序[/b].這是乙個非常重要的知識點,當你發現識別符號的右邊緊挨著乙個左圓括號(請一定注意到我這裡寫的是左圓括號[/b])或者是方括號的時候,你就不要有任何懷疑的告訴自己,識別符號是乙個(返回…的函式)或者陣列。讓我們用這樣的思維再來分析一下char next();這個宣告,首先分析識別符號next,得出next是…。

,然後向右看它右邊緊靠它的是不是圓括號或者方括號,這個例子中是乙個圓括號,所以我們得出next是乙個返回…。的函式,最後通過char型別符得出,next是乙個返回char型變數的函式,至此,整個宣告分析完成。

好了,現在讓我們來總結宣告分析中的前兩個基本步驟,也是關鍵步驟。

首先,分析宣告中的識別符號,這裡有乙個問題,不知道大家發現沒,我上面舉的例子中都是只有乙個識別符號,如果宣告中出現多個識別符號怎麼處理,比如說在next函式中增加幾個引數,如char next(int a,int b);這樣的話,整個宣告中有三個識別符號,我們究竟首先選取分析哪個識別符號呢。通過這個簡單的例子,我們很容易觀察出,首先選擇的是最左邊的識別符號,當然,我們決不能根據乙個特例就得出普遍結論,在這裡,我以乙個已經掌握了這個知識點的人的身份告訴你,你的猜想是正確的,識別符號從最左邊的開始處理,c中確實是這樣做的。那麼現在,你也掌握了這個知識點,讓我們繼續向下討論。

當我們選定識別符號後,就要觀察緊靠它右邊的宣告器(也就是在宣告中出現的各種各樣的符號和變數,如(),*,const,等的官方說法),這裡分兩種情況:如果出現的是乙個左圓括號,那麼識別符號就是乙個返回…。的函式,如果出現的是乙個方括號,則毫無疑問的說明,識別符號是乙個陣列,至於是乙個什麼樣的陣列,則必須通過之後的分析才能夠知道。

好了,現在又出現了乙個新的問題,那就是如果識別符號的緊鄰的右邊既不是左圓括號,也不是方括號怎麼辦?比如是乙個右圓括號呢。這就需要我們的第三個比較重要的分析宣告的步驟。

那就是:看識別符號左邊的符號情況,分以下兩種:a, 如果緊鄰識別符號左邊的是乙個左圓括號,則找尋到和它匹配的右圓括號,將整個括號內的宣告當成乙個整體分析。

b, 如果緊鄰識別符號左邊的是*,或者const,或者volatile三者之一,則繼續向左查詢,直到宣告器超出三者之外為止。也就是說要一直找到某個符號既不是*,const,也不是volatile為止。

c, 繼b之後,如果符號是左圓括號,則回到a進行處理。

最後,剩下的符號可以一併閱讀,因為那一定已經是非常容易理解的了。

在這裡,我想為初學朋友解釋一下const和volatile兩個修飾符,const表示所修飾的變數是唯讀的,也就是一經賦值就不能再被修改。例如const char p; const char *p;此處需要大家注意的就是,const char *p;和char * const p;是不同的宣告,前乙個表明指標p所指向的內容是唯讀的,而後乙個則表示指標本身是唯讀的,而它指向的內容則是可以改變的。我總結了乙個規律供大家記憶方便,那就是如果 *和識別符號是乙個整體,沒有被任何東西分開,則說明const修飾的是指標指向的內容,如上例中的const char *p;或者是char const *p;*和識別符號(p)沒有被const這個修飾符分開,則說明const修飾的是指標指向的內容,而一旦*和識別符號被分開了,則說明const修飾的是指標本身,而指標指向的變數則是可變的。

除非指向的變數本身也被const修飾了,如const char * const p;第二個修飾符volatile表明修飾的變數是他是可以被本程式和別的程式改變資料,象系統時間,不管這個程式是不是斷點,sleep,別的程式都改變他的值。

好了,如果大家仔細閱讀了上面的部分,一定會發現其實細心去分析之後,宣告的理解也並不如何困難,現在我們先來用乙個比較常規的宣告來熟練一下上面的思維方式,最後我們以解決本文開始提到的兩個宣告作為結尾。

char (*p)(); //首先看最左邊的識別符號p,(表明p是……)他的右邊既不是左圓括號也不是方括號,於是看左邊,發現是*,根據上面的原則(如果緊鄰識別符號左邊的是*,或者const,或者volatile三者之一,則繼續向左查詢,直到宣告器超出三者之外為止。也就是說要一直找到某個符號既不是*,const,也不是volatile為止。)我們繼續向左查詢,發現下乙個符號是(, 於是根據原則(繼b之後,如果符號是左圓括號,則回到a進行處理。

)我們找尋匹配這個左圓括號的右圓括號,那麼,我們就可以把這個括號裡的東西作為乙個整體來處理,該例子中括號內的內容為*p,所以我們得出p是乙個指標,既然是乙個指標,就肯定指向某一樣東西,而這個東西,我們必須繼續**才能把它給挖出來,現在我們已經把括號內的東西當成乙個整理處理了,就相當它是乙個變數(我們通過分析這個括號內的東西確實得出了乙個指標型別的變數,)於是我們可以把它當成最初的識別符號,回到第一步分析,看這個變數(實際上是把整個括號看成乙個識別符號,分析這個括號最靠近右邊的符號)左邊臨近的是否是左圓括號或者是方括號,很幸運的,我們發現了左圓括號,就說明這個變數(也就是整個括號)是乙個返回…的函式,而原來那個等價於變數的括號內部事實上是乙個指標變數,再結合我們剛才分析出的,就可以知道,這個指標變數指向乙個返回…。的函式,最後我們分析這個函式究竟返回什麼,這時候只剩下乙個char型別符了,所以整個宣告的內容是,乙個指向函式的指標,該函式返回乙個char型的變數分析出這樣乙個宣告並不困難,難就難在用上面的思維進行分析,上面的分析步驟事實上是編譯器進行宣告分析的步驟,但其實就上面總結的還並不完全。那麼下面我就將完整的宣告的分析步驟羅列出來:

分析的步驟匹配的符號閱讀方式1 取最左邊的識別符號識別符號表示識別符號是……

2檢視識別符號右邊下乙個 [可能的大小對於每一對,表示符號,如果是方括號 ……的陣列3 如果是乙個左圓括號 ( 可能的引數) 到右括號為止的內容表示返回……的函式4 如果左邊的符號是乙個 ( 這個左括號把已經處理的左圓括號部分宣告組合在一起,直到遇見對應的右括號,然後從第二步重新開始5 如果左邊的符號是下述符號之一*,const,volatile *,const,volatile 繼續向左讀符號,知道宣告器超出三者之外,然後重複第4步6 剩下的符號形成基本型別基本型別如char,int 剩餘的符號可一併閱讀

以上就是宣告分析的完整總結版,也是c專家程式設計書中羅列出來的,我這裡只是借鑑一下,供大家學習參考。

好了,有了以上的基礎,我們就好分析第乙個恐怖型的宣告,從這樣的宣告中,我們上面的分析步驟的優勢就體現出來了

void(*signal(int sig, void (*func)(int)))(int);

首先,最左邊的識別符號是signal,表明signal是…。,緊靠它右邊的是乙個左圓括號,說明signal是乙個返回…的函式,而int sig, void (*func)(int)是這個函式的引數,裡面的每乙個部分都可以重新用我們的方法進行分析,這裡就不敘述了。然後我們根據上面的分析步驟,再看左邊的符號是什麼,是乙個指標符*,表明該函式是乙個返回乙個指標的函式,此時,函式已經被我們簡化成void p(int);j其中p是乙個返回指標的函式,是我們上面分析所得出的結果。

那麼該指標指向的是什麼呢,這裡再根據前面的步驟,分析整個括號的臨近右邊的符號,也就是p(我們已經將整個括號中的內容等價為p這個假想中的識別符號)右邊的符號,發現是乙個左圓括號,所以p是乙個返回…的函式,而p是乙個指標(實際上是某乙個函式所返回的指標),所以p是乙個指向函式的指標,最後根據void判斷出,該函式的返回值為空。這樣,乙個複雜的宣告就被我們抽絲剝繭的分析完了、用完整版的語言來敘述這個宣告:這是乙個函式,該函式有兩個引數(引數的宣告分析大家自己完成哈),並且該函式返回乙個指標,該指標指向乙個函式,該函式有乙個引數,並且返回型別為空。

關於C語言中的變數

include void f5 int static int e void f4 int a void main void f4 int int e void f5 int b 首先,main函式中以10為引數呼叫函式f5,在函式f5中,給變數e賦值為5,接下來呼叫函式f4,引數b的值為10,在函式...

C語言中static變數使用方法

1.static 變數 靜態變數的型別說明符是static。靜態變數當然是屬於靜態儲存方式,但是屬於靜態儲存方式的量不一定就是靜態變數。例如外部變數雖屬於靜態儲存方式,但不一定是靜態變數,必須由 static加以定義後才能成為靜態外部變數,或稱靜態全域性變數。2.靜態區域性變數 靜態區域性變數屬於靜...

C語言中static變數使用方法

1.static 變數 靜態變數的型別說明符是static。靜態變數當然是屬於靜態儲存方式,但是屬於靜態儲存方式的量不一定就是靜態變數。例如外部變數雖屬於靜態儲存方式,但不一定是靜態變數,必須由 static加以定義後才能成為靜態外部變數,或稱靜態全域性變數。2.靜態區域性變數 靜態區域性變數屬於靜...