C:表達式、語句、聲明


  有網友在http://www.cnblogs.com/pmer/archive/2013/03/15/2960809.html 129樓問

  “表達式、語句、聲明之間的區別到底是什么?”

  這個問題對很多人來說確實很模糊,甚至很多出版物中也有很多錯誤的講述,故此本文力圖對此做一詳盡說明和澄清。

表達式(Expression)

     根據C標准,表達式(Expression)是運算符(operator)和操作數(operand)所構成的序列,例如"3+2"。最簡單的情形,也可能沒有運算符,例如“3”。

     可能需要特意提一下的是,函數調用也是表達式。例如 sqrt(1.0),這里的“()”是一個運算符,sqrt和1.0則是“()”這個運算符的操作數。

     表達式所表達的可能是要求計算機進行值的計算(computation of a value),例如“3+2”這個表達式,表示的就是要求計算機求3+2的值。當然,表達式同時也表示這個計算所得到的值。 

     表達式還可能用來指明一個數據對象(object)或一個函數(function)。例如,若有 int i; 那么在 i = 1 這個表達式中的 i 這個子表達式就是指 i 所代表的那個object——一塊連續的內存。再比如, 表達式 & printf 中,printf這個子表達式表示的是相應的printf()庫函數。

     除此之外,表達式可能產生副效應(side effects),例如printf("ABC") 這個表達式的值是3,其副效應是在標准輸出設備上連續輸出A、B、C這三個字符。再比如,對於 int i; 表達式 i = 3 的值是3,副效應是 i 所代表的數據對象被寫入了3。

語句(Statement)

    C語言語句(Statement)的定義從其功能上來說有些含糊:語句規定的是一種“動作”(action)。這種動作最顯著的特點是規定了如何跳轉或結束。例如,對於int i; "i = 1 ;"這條語句的意義是到“;”位置時 i = 1 這個表達式求值(Evaluate), 即值的計算和副效應必須完成。再比如“return ;”語句規定的是跳轉到另一位置繼續執行。

     盡管語句的功能有些含糊,但其語法形式確實極其清晰的。形式上較為簡單的語句有:

     表達式語句(expression-statement),形式為:

     expression ;

     其中的表達式是可選的。這種語句以“;”作為結束標志。對於初學者來說最常見的表達式語句是調用printf()函數所形成的:printf("Hello World\n");

     跳轉語句(jump-statement),包括:

     goto 語句、continue 語句、break 語句和return 語句。這種語句也以“;”作為結束標志。         

     復合語句(compound-statement),其一般形式為:

     {block-item-list}

     其中的block-item-list可以有也可以沒有,可以是聲明,也可以是語句。

     有一點特別需要說明,復合語句並非以“;”作為結束標志。這表明語句中不一定有“;”。國內C語言書中有一種常見的陳詞濫調:“一個語句必須在最后有一個分號,分號是語句中不可缺少的組成部分”(譚浩強,《C程序設計》第四版,p58),那絕對在是瞪着眼睛說胡話。因為復合語句的最后就不需要有分號。最簡單的復合語句只有一對“{}”,根本就沒分號什么事兒。

     其他的語句都是構造性的,即是在其他語句的基礎上構造出來的。比如標號語句(labeled-statement),就是在語句前加一冒號及其他內容:

     identifier : statement

     case constant-expression : statement

     default : statement

     選擇語句也是依據類似的原則在語句的基礎上構造的:

     if ( expression ) statement
     if ( expression ) statement else statement
     switch ( expression ) statement

     值得一提的是switch語句並不一定是在復合語句的基礎上構造的,這和很多人的常識不同。例如,

     switch ( i ) case 0:case 1:case 2:printf("ABC\n");

     就是一條完全合法的switch語句。  其功能等價於

     if (i==0||i==1||i==2)  printf("ABC\n");

     C語言的循環語句同樣是在語句的基礎上構造而成。

      while ( expression ) statement
  do statement while ( expression ) ;
      for ( expressionopt ; expression ; expression ) statement
      for ( declaration expression; expression ) statement

      在這些語句中,只有do-while語句最后一定是以“;”作為結束標志。

      總之,由於復合語句並不是以分號作為結束標志,而很多語句都是在語句的基礎上進一步構造而成,因此除了表達式語句、跳轉語句及do-while語句一定是以分號結束,其他語句則可能以分號結束,也可能不以分號結束,分號並不是語句必須的組成部分。

聲明(Declarations)

  除了static_assert declaration(C11),聲明的作用都是向編譯器解釋一個或多個標識符的含義及屬性。

      C標准給出的聲明的一般形式為

      declaration-specifiers init-declarator-list ;

      因此,通常情況下聲明都有“;”。

      但實際上,函數定義同時也是聲明(A definition of an identifier is a declaration),在格式上卻與此不符。

      盡管功能明確,形式簡單,但聲明其實是C語言中最復雜的成分,至少是之一。這種復雜性被 declarator 漠然地掩蓋起來了。 

      C語言要求每個聲明至少要聲明一個 declarator 、一個tag或一個枚舉成員。也就是說

      int i ;//沒問題,i是declarator

      int ; //違法,無declarator

      struct t;//合法,聲明了一個tag——t

      enum { A };//是合法的,聲明了枚舉成員A

      struct { int a ;}; //違背語言要求,可能招致警告。(在C語言早期,並不違反標准)

      union { int a ;};  //違背語言要求,可能招致警告。(在C語言早期,並不違反標准) 

      聲明不是語句(《C Primer Plus》一書把聲明稱為“聲明語句”,明顯是把C++的概念張冠李戴到C上面了)。一個簡單的證明是,聲明無法按照語句規則構成語句。譬如

      if( 1 ) int i ; //這在C語言中是不成立的

      有些聲明也叫定義(definition),所有的定義都是聲明。包括

      導致保留存儲區的對象聲明。最典型的就是局部變量的聲明。

      含有函數體的函數聲明,即函數定義。函數定義盡管在形式上不符合由“;”結尾的形式,但同樣是一個聲明,有時也被稱之為外部聲明(external declaration)或外部定義(external definition)。但習慣上,很多人所說的“函數聲明”往往特指那些非定義式的函數聲明。

      除此之外,枚舉常量(enumeration constant)和typedef name也是定義。

      declaration-specifiers包括存儲類別說明符(storage class specifier),類型說明符(type specifier) ,類型限定符(type qualifier),函數說明符(function specifier),從C11起又增加了一個對齊說明符(alignment specifier)。

      和多數人常識不符的是,typedef也屬於存儲類別說明符(storage class specifier)。

      把typedef類型定義歸為聲明並把typedef關鍵字作為存儲類別說明符(storage class specifier)只是為了語法描述上的方便。實際上typedef並沒有像其他幾個存儲類別說明符(extern,static,auto,register,_Thread_local(C11))那樣對存儲類別做出任何說明。

      在每個聲明中,存儲類別說明符只能出現一次。C11引人新的存儲類別說明符_Thread_local之后,這個說法不再成立了。

      類型說明符(type specifier)包括:void、char、short、int、long、float、double、signed、unsigned、_Bool、_Complex、atomic-type-specifier、struct-or-union-specifier、enum-specifier、typedef-name,使用時可能是類型說明符的某種組合,譬如 long double。其中_Bool、_Complex是C99新增的,最初還增加了一個_Imaginary,后來又刪除了。atomic-type-specifier是C11新增的。此外,C11在struct-or-union聲明中正式支持了匿名結構體或聯合體(anonymous structures and unions)。

      類型限定符(type qualifier)在C90中只有const和volatile兩個,const借鑒的是C++,volatile則完全是C標准委員會的發明。C99增加了一個restrict,只用於指針類型,提出這個限定符的目的是更好地優化。C又增加了一個_Atomic類型限定符。這個限定符也是唯一的一個Atomic type specifiers,這種Atomic 類型說明符是C11新增的內容。

      函數說明符(function specifier)是C99之后出現的。C99增加了一個inline這個函數說明符,C11又新增了一個_Noreturn函數說明符用來說明不返回調用者的函數,例如exit()函數。

     C11增加的另一個新的聲明說明符是對齊說明符(alignment specifier)。對齊問題不再像以前那樣猶抱琵琶半遮面,而是直接擺到了桌面上。

     聲明的init-declarator-list部分由一個或多個用“,”分隔的init-declarator組成。init-declarator可以是一個單獨的declarator或初始化形式declarator = initializer。

     最簡單的declarator是一個標識符。這是一種direct-declarator。在direct-declarator前面可以加* type-qualifier-list用以聲明指針。聲明指針時type-qualifier置於*的右側。而在declaration-specifiers中則沒有這樣的次序要求。

     direct-declarator還可以具有下面幾種形式:

     ( declarator )
     direct-declarator [ type-qualifier-list assignment-expressionopt ]
     direct-declarator [ static type-qualifier-list assignment-expression ]
     direct-declarator [ type-qualifier-list static assignment-expression ]
     direct-declarator [ type-qualifier-list * ]
     direct-declarator ( parameter-type-list )
     direct-declarator ( identifier-list )

     第一種中的“()”可能改變對declarator的類型解釋(例如:int *p[1];int (*p)[1];)。     

     自C99起,不再要求聲明數組時[]內是整數常量表達式,可以用變量描述數組尺寸。例如 int a[n]; 這就是所謂的VLA。VLA是C99的正式特性,在C11中則是一個可選特性。編譯器可以支持,也可以不支持。至於[]內的* 、static及type-qualifier-list,只用於聲明函數參數時。

     函數聲明“()”內雖然可以有parameter-type-list和 identifier-list兩種形式,但后者其實是一種正在逐步廢棄的形式,即

     void f(a)

     int a;

     {/*……*/}
    這種形式。除了在老代碼中可以看到,現在已經很少有人這樣寫了。現代風格的C語言提倡函數原型風格的聲明,即 參數為parameter-type-list這種形式。這種形式有兩種:

     parameter-list

     或
     parameter-list , ...

     “...”用於聲明參數個數不定的函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM