在C++中,所有的代碼都是通過辨別符(Identifier)、表達式(Expression)和語句(Statement)及一些必要的符号(如大括号等)組成,在此先說明何謂辨別符。
辨別符
辨別符是一個字母序列,由大小寫英文字母、下劃線及數字組成,用于辨別。辨別就是标出并識别,也就是名字。其可以作為後面将提到的變量或者函數或者類等的名字,也就是說用來辨別某個特定的變量或者函數或者類等C++中的元素。
比如:abc就是一個合法的辨別符,即abc可以作為變量、函數等元素的名字,但并不代表abc就是某個變量或函數的名字,而所謂的合法就是任何一個辨別符都必須不能以數字開頭,隻能包括大小寫英文字母、下劃線及數字,不能有其它符号,如,!^等,并且不能與C++關鍵字相同。也就是我們在給一個變量或函數起名字的時候,必須将起的名字看作是一個辨別符,并進而必須滿足上面提出的要求。如12ab_C就不是一個合法的辨別符,是以我們不能給某個變量或函數起12ab_C這樣的名字;ab_12C就是合法的辨別符,是以可以被用作變量或函數的名字。

C++程式入門——表達式講解
前面提到關鍵字,在後續的語句及一些聲明修飾符的介紹中将發現,C++提供了一些特殊的辨別符作為語句的名字,用以辨別某一特定語句,如if、while等;或者提供一些修飾符用以修飾變量、函數等元素以實作語義或給編譯器及連接配接器提供一些特定資訊以進行優化、查錯等操作,如extern、static等。是以在命名變量或函數或其他元素時,不能使用if、extern等這種C++關鍵字作為名字,否則将導緻編譯器無法确認是一個變量(或函數或其它C++元素)還是一條語句,進而無法編譯。
如果要讓某個辨別符是特定變量或函數或類的名字,就需要使用聲明,在後續的文章中再具體說明。
數字
C++作為電腦程式設計語言,電腦是處理數字的,是以C++中的基礎東西就是數字。C++中提供兩種數字:整型數和浮點數,也就是整數和小數。但由于電腦實際并不是想象中的數字化的(詳情參見《C++從零開始(三)》中的類型一節),是以整型數又分成了有符号和無符号整型數,而浮點數則由精度的差別而分成單精度和雙精度浮點數,同樣的整型數也根據長度分成長整型和短整型。
要在C++代碼中表示一個數字,直接書寫數字即可,如:123、34.23、-34.34等。由于電腦并非以數字為基礎而導緻了前面數字的分類,為了在代碼中表現出來,C++提供了一系列的字尾進行表示,如下:
u或U 表示數字是無符号整型數,如:123u,但并不說明是長整型還是短整型
l或L 表示數字是長整型數,如:123l;而123ul就是無符号長整型數;而34.4l就是長雙精度浮點數,等效于雙精度浮點數
i64或I64 表示數字是長長整型數,其是為64位作業系統定義的,長度比長整型數長。如:43i64
f或F 表示數字是單精度浮點數,如:12.3f
e或E 表示數字的次幂,如:34.4e-2就是0.344;0.2544e3f表示一個單精度浮點數,值為254.4
當什麼字尾都沒寫時,則根據有無小數點及位數來決定其具體類型,如:123表示的是有符号整型數,而12341434則是有符号長整型數;而34.43表示雙精度浮點數。
為什麼要搞這麼多事出來,還分什麼有符号無符号之類的?這全是因為電腦并非基于數字的,而是基于狀态的,詳情在下篇中将詳細說明。
作為科學計算,可能經常會碰到使用非十進制數字,如16進制、8進制等,C++也為此提供了一些字首以進行支援。
在數字前面加上0x或0X表示這個數字是16進制表示的,如:0xF3Fa、0x11cF。而在前面加一個0則表示這個數字是用8進制表示的,如:0347,變為十進制數就為231。但16進制和8進制都不能用于表示浮點數,隻能表示整型數,即0x34.343是錯誤的。
字元串
C++除了提供數字這種最基礎的表示方式外,還提供了字元及字元串。這完全隻是出于友善編寫程式而提供的,C++作為電腦語言,根本沒有提供字元串的必要性。不過由于人對電腦的基本要求就是顯示結果,而字元和字元串都由于是人易讀的符号而被用于顯示結果,是以C++專門提供了對字元串的支援。
前面說過,電腦隻認識數字,而字元就是文字元号,是一種圖形符号。為了使電腦能夠處理符号,必須通過某種方式将符号變成數字,在電腦中這通過在符号和數字之間建立一個映射來實作,也就是一個表格。表格有兩列,一列就是我們欲顯示的圖形符号,而另一列就是一個數字,通過這麼一張表就可以在圖形符号和數字之間建立映射。現在已經定義出一标準表,稱為ASCII碼表,幾乎所有的電腦硬體都支援這個轉換表以将數字變成符号進而顯示計算結果。
有了上面的表,當想說明結果為“A”時,就查ASCII碼表,得到“A”這個圖形符号對應的數字是65,然後就告訴電腦輸出序号為65的字元,最後螢幕上顯示“A”。
這明顯地繁雜得異常,為此C++就提供了字元和字元串。當我們想得到某一個圖形符号的ASCII碼表的序号時,隻需通過單引号将那個字元括起來即可,如:'A',其效果和65是一樣的。當要使用不止一個字元時,則用雙引号将多個字元括起來,也就是所謂的字元串了,如:"ABC"。是以字元串就是多個字元連起來而已。但根據前面的說明易發現,字元串也需要映射成數字,但它的映射就不像字元那麼簡單可以通過查表就搞定的,對于此,将在後續文章中對數組作過介紹後再說明。
操作符
電腦的基本是數字,那麼電腦的所有操作都是改變數字,是以很正常地C++提供了操作數字的一些基本操作,稱作操作符(Operator),如:+ - * / 等。任何操作符都要傳回一個數字,稱為操作符的傳回值,是以操作符就是操作數字并傳回數字的符号。作為一般性地分類,按操作符同時作用的數字個數分為一進制、二進制和三元操作符。
一進制操作符有:
+
其後接數字,原封不動地傳回後接的數字。如: +4.4f的傳回值是4.4;+-9.3f的傳回值是-9.3。完全是出于語義的需要,如表示此數為正數。
-
其後接數字,将後接的數字的符号取反。如: -34.4f的傳回值是-34.4;-(-54)的傳回值是54。用于表示負數。
!
其後接數字,邏輯取反後接的數字。邏輯值就是“真”或“假”,為了用數字表示邏輯值,在 C++中規定,非零值即為邏輯真,而零則為邏輯假。是以3、43.4、'A'都表示邏輯真,而0則表示邏輯假。邏輯值被應用于後續的判斷及循環語句中。而邏輯取反就是先判斷“!”後面接的數字是邏輯真還是邏輯假,然後再将相應值取反。如: !5的傳回值是0,因為先由5非零而知是邏輯真,然後取反得邏輯假,故最後傳回0。 !!345.4的傳回值是1,先因345.4非零得邏輯真,取反後得邏輯假,再取反得邏輯真。雖然隻要非零就是邏輯真,但作為編譯器傳回的邏輯真,其一律使用1來代表邏輯真。
~
其後接數字,取反後接的數字。取反是邏輯中定義的操作,不能應用于數字。為了對數字應用取反操作,電腦中将數字用二進制表示,然後對數字的每一位進行取反操作(因為二進制數的每一位都隻能為1或0,正好符合邏輯的真和假)。如~123的傳回值就為-124。先将123轉成二進制數01111011,然後各位取反得10000100,最後得-124。這裡的問題就是為什麼是8位而不是16位二進制數。因為123小于128,被定位為char類型,故為8位(關于char是什麼将下篇介紹)。如果是~123ul,則傳回值為4294967172。 為什麼要有數字取反這個操作?因為CPU提供了這樣的指令。并且其還有着很不錯且很重要的應用,後面将介紹。
關于其他的一進制操作符将在後續文章中陸續提到(但不一定全部提到)。
二進制操作符有:
+-*/%
其前後各接一數字,傳回兩數字之和、差、積、商、餘數。如:34+4.4f的傳回值是38.4;3+-9.3f的傳回值是-6.3。34-4的傳回值是30;5-234的傳回值是-229。3*2的傳回值是6;10/3的傳回值是3。10%3的傳回值是1;20%7的傳回值是6。
&&||
其前後各接一邏輯值,傳回兩邏輯值之“與”運算邏輯值和“或”運算邏輯值。如:'A'&&34.3f的傳回值是邏輯真,為1;34&&0的傳回值是邏輯假,為0。0||'B'的傳回值是邏輯真,為 1;0||0的傳回值是邏輯假,為0。
&|^
其前後各接一數字,傳回兩數字之“與”運算、“或”運算、“異或”運算值。如前面所說,先将兩側的數字轉成二進制數,然後對各位進行與、或、異或操作。如:4&6的傳回值是4,4轉為00000100,6轉為00000110各位相與得,00000100,為4。4|6的傳回值是6,4轉為00000100,6轉為00000110各位相或得,00000110,為6。4^6的傳回值是2,4轉為00000100,6轉為00000110各位相異或得,00000010,為2。
<==>=<=!=
其前後各接一數字,根據兩數字是否大于、小于、等于、大于等于、小于等于及不等于而傳回相應的邏輯值。如:34>34的傳回值是0,為邏輯假;32<345的傳回值為1,為邏輯真。23>=23和23>=14的傳回值都是1,為邏輯真;54<=4的傳回值為0,為邏輯假。56==6的傳回值是0,為邏輯假;45==45的傳回值是1,為邏輯真。5!=5的傳回值是0,為邏輯假;5!=35的傳回值是真,為邏輯真。
<<
其前後各接一數字,将左側數字右移或左移右側數字指定的位數。與前面的 ~、&、|等操作一樣,之是以要提供左移、右移操作主要是因為CPU提供了這些指令,主要用于編一些基于二進制數的算法。 <<将左側的數字轉成二進制數,然後将各位向左移動右側數值的位數,如:4,轉為00000100,左移2位,則變成00010000,得16。 >>與<<一樣,隻不過是向右移動罷了。如:6,轉為00000110,右移1位,變成00000011,得3。如果移2位,則有一位超出,将截斷,則6>>2的傳回值就是00000001,為1。 左移和右移有什麼用?用于一些基于二進制數的算法,不過還可以順便作為一個簡單的優化手段。考慮十進制數3524,我們将它左移2位,變成352400,比原數擴大了100倍,準确的說應該是擴大了10的2次方倍。如果将3524右移2位,變成35,相當于原數除以100的商。同樣,前面4>>2,等效于4/4的商;32>>3相當于32/8,即相當于32除以2的3次方的商。而4<<2等效于4*4,相當于4乘以2的2次方。是以左移和右移相當于乘法和除法,隻不過隻能是乘或除相應進制數的次方罷了,但它的運作速度卻遠遠高于乘法和除法,是以說它是一種簡單的優化手段。
,
其前後各接一數字,簡單的傳回其右側的數字。如: 34.45f,54的傳回值是54;-324,4545f的傳回值是4545f。 那它到底有什麼用?用于将多個數字整和成一個數字,在《C++從零開始(四)》中将進一步說明。 關于其他的二進制操作符将在後續文章中陸續提到(但不一定全部提到)。 三元操作符隻有一個,為?:,其格式為:<數字1>?<數字2>:<數字3>。它的傳回值為:如果<數字1>是邏輯真,傳回<數字2>,否則傳回<數字3>。如: 34?4:2的傳回值就是4,因為34非零,為邏輯真,傳回4。而0?4:2的傳回值就是2,因為0為邏輯假,傳回2。
表達式
你應該發現前面的荒謬之處了——12>435傳回值為0,那為什麼不直接寫0還吃飽了撐了寫個12>435在那?這就是表達式的意義了。
前面說“>”的前後各接一數字,但是操作符是操作數字并傳回數字的符号,因為它傳回數字,是以可以放在上面說的任何一個要求接數字的地方,也就形成了所謂的表達式。如:2354/45>34的傳回值就是0,因為2354的傳回值為1242;然後又将1242作為“/”的左接數字,得到新的傳回值27.6;最後将27.6作為“>”的左接數字進而得到傳回值0,為邏輯假。
是以表達式就是由一系列傳回數字的東西和操作符組合而成的一段代碼,其由于是由操作符組成的,故一定傳回值。而前面說的“傳回數字的東西”則可以是另一個表達式,或者一個變量,或者一個具有傳回值的函數,或者具有數字類型操作符重載的類的對象等,反正隻要是能傳回一個數字的東西。如果對于何謂變量、函數、類等這些名詞感到陌生,不需要去管它們,在後繼的文章中将會一一說明。
是以34也是一個表達式,其傳回值為34,隻不過是沒有操作符的表達式罷了(在後面将會了解到34其實是一種操作符)。故表達式的概念其實是很廣的,隻要有傳回值的東西就可以稱為表達式。
由于表達式裡有很多操作符,執行操作符的順序依賴于操作符的優先級,就和數學中的一樣,*、/的優先級大于+、-,而+、-又大于>、<等邏輯操作符。不用去刻意記住操作符的優先級,當不能确定操作符的執行順序時,可以使用小括号來進行指定。如:
((1+2)3)+3)/4的傳回值為3,而1+23+3/4的傳回值為7。注意3/4為0,因為3/4的商是0。當希望進行浮點數除法或乘法時,隻需讓操作數中的某一個為浮點數即可,如:3/4.0的傳回值為0.75。
& | ^ ~等的應用
前面提過邏輯操作符“&&”、“||”、“!”等,作為表示邏輯,其被C++提供一點都不值得驚奇。但是為什麼要有一個将數字轉成二進制數,然後對二進制數的各位進行邏輯操作的這麼一類操作符呢?首先是CPU提供了相應的指令,并且其還有着下面這個非常有意義的應用。
考慮一十字路口,每個路口有三盞紅綠燈,分别指明能否左轉、右轉及直行。共有12盞,現在要為它編寫一個控制程式,不管這程式的功能怎樣,首先需要将紅綠燈的狀态轉化為數字,因為電腦隻知道數字。是以用3個數字分别表示某路口的三盞紅綠燈,是以每個紅綠燈的狀态由一個數字來表示,假設紅燈為0,綠燈為1(不考慮黃燈或其他情況)。
後來忽然發現,其實也可以用一個數字表示一個路口的三盞紅綠燈狀态,如用110表示左轉綠燈、直行綠燈而右轉紅燈。上面的110是一個十進制數字,它的每一位實際都可以為0~9十個數字,但是這裡隻應用到了兩個:0和1,感覺很浪費。故選擇二進制數來表示,還是110,但是是二進制數了,轉成十進制數為6,即使當為111時轉成十進制數也隻是7,比前面的110這個十進制數小多了,節約了……??什麼??
我們在紙上寫數字235425234一定比寫134這個數字要更多地占用紙張(假設字都一樣大)。是以記錄一個大的數比記錄一個小的數要花費更多的資源。簡直荒謬!不管是100還是1000,都隻是一個數字,為什麼記錄大的數字就更費資源?因為電腦并不是數字計算機,而是電子計算機,它是基于狀态而不是基于數字的,這在下篇會詳細說明。電腦必須使用某種表示方式來代表一個數字,而那個表示方式和二進制很像,但并不是二進制數,故出現記錄大的數較小的數更耗資源,這也就是為什麼上面整型數要分什麼長整型短整型的原因了。
下面繼續上面的思考。使用了110這個二進制數來表示三盞紅綠燈的狀态,那麼現在要知道110這個數字代表左轉紅綠燈的什麼狀态。以數字的第三位表示左轉,不過電腦并不知道這個,是以如下:110&100。這個表達式的傳回值是100,非零,邏輯真。假設某路口的狀态為010,則同樣的010&100,傳回值為0,邏輯假。是以使用“&”操作符可以将二進制數中的某一位或幾位的狀态提取出來。是以我們要了解一個數字代表 的紅綠燈狀态中的左轉紅綠燈是否綠燈時,隻需讓它和100相與即可。
現在要保持其他紅綠燈的狀态不變,僅僅使左轉紅綠燈為綠燈,如目前狀态為010,為了使左轉紅綠燈為綠燈,值應該為110,這可以通過010|100做到。如果目前狀态是001,則001|100為101,正确——直行和右轉的紅綠燈狀态均沒有發生變化。是以使用“|”操作符可以給一個二進制數中的某一位或幾位設定狀态,但隻能設定為1,如果想設定為0,如101,要關掉左轉的綠燈,則101&~100,傳回值為001。
上面一直提到的路口紅綠燈的狀态實際編寫時可以使用一個變量來表示,而上面的100也可以用一個辨別符來表示,如state&TS_LEFT,就可以表示檢查變量state所表示的狀态中的左轉紅綠燈的狀态。
上面的這種方法被大量地運用,如建立一個視窗,一個視窗可能有二三十個風格,則通過上面的方法,就可以隻用一個32位長的二進制數字就表示了視窗的風格,而不用去弄二三十個數字來分别代表每種風格是否具有。