在C++中,除了需要用算術操作符對數據進行加減乘除的算術操作之外,我們有時候還需要對數據之間的關系進行操作,也就是對兩個數據進行大小比較,得出它們之間的大小關系。在現實世界中,這種大小關系的比較是非常常見的。例如,這家攤位上的西紅柿5元一斤,而另外一家相同的西紅柿卻只賣3元一斤,5和3一比較,就知道第二家的西紅柿更便宜了。我們說,程序是用來抽象和描述現實世界的,為了在程序中表達這種大小關系的比較,C++專門提供了關系操作符,包括“>”(大於)、“>=”(大於或等於)、“<”(小於)、“<=”(小於或等於)、“==”(等於)、“!=”(不等於)。
最佳實踐:注意“==”和“=”的區別
這兩個符號在形式上非常相似,但是所表達的意義卻是千差萬別:
“==”是判斷兩個值是否相等的關系操作符;
“=”則是用其右邊的值對左邊的變量進行賦值的賦值操作符。
因為兩者在形式上的相似性,如果不注意,很容易將“==”誤寫作“=”,或是將“=”誤寫作“==”,從而導致代碼無法正確地表達設計者的意圖,最終導致程序錯誤。更讓人頭疼得是,編譯器無法發現這種隱秘的語義上的錯誤,即使在代碼中出現了誤用,編譯器也不會給出錯誤信息,這使得我們防范這種錯誤的發生變得更加困難。例如:
int a; // 程序員本來的意圖是將a賦值為1 // 結果卻將“=”誤寫成了“==”, // 代碼意義成了將a和1進行相等比較 a == 1; // 程序員本來的意圖是將a和1進行相等比較 // 結果卻將“==”誤寫成了“=”,代碼意義成了將a賦值為1 if(a = 1) { cout<<"a等於1"<<endl; }
從這段代碼的實際意義上來看,整個都是在胡言亂語,但是從代碼的執行結果上來看,卻與我們的設計預期相符,成功地輸出了“a等於1”。同時,編譯器也不會報告任何錯誤信息,這使得這一錯誤更具隱秘性。
要杜絕第一種誤用,只有靠我們在書寫代碼時多加注意。另外,如果是給變量賦初始值,最好是在定義變量的時候同時進行。例如:
// 定義變量的同時賦初始值 // 在這種形式下,編譯器會幫助發現“int a == 1;”這樣的錯誤 int a = 1;
而對於第二種誤用,同樣需要我們在書寫代碼時小心謹慎。此外,有一種特殊情況是,當我們在將變量與某個常量進行比較時,我們最好是將常量放在等號的左邊。這樣,因為不能對常量進行賦值,即使我們將“==”誤寫成了“=”,編譯器也會幫助我們發現這種誤用。例如:
// 將常量1同變量a進行相等比較 // 如果誤寫作“if( 1 = a )”,就成了對常量1進行賦值, // 而常量是不能被賦值的,編譯器會報告一個錯誤信息,幫助我們發現這種誤用 if( 1 == a) { cout<<"a等於1"<<endl; }
此外,雖然編譯器對這種誤用沒有錯誤信息,但是卻有相應的警告信息。在編譯代碼的時候,打開相應的警告信息開關(使用gcc編譯器的-Wall編譯選項打開全部警告開關),並注意編譯器輸出的警告信息,也可以很好地防止這種誤用的發生。
關系操作符是二元操作符,其作用是將兩個操作數進行關系運算,比較兩個操作數的大小或者是否相等,其運算結果是bool類型的true或者false。如果兩個操作數的大小關系符合操作符所表達的大小關系,則表達式的結果為true,反之則為false。例如:
// 兩個攤位上的西紅柿價格 int a = 5; int b = 3; // 對a和b的值進行小於比較,但a的值大於b的值,不符合操作符“<”的意義, // 所以表達式“a < b”的計算結果值為false,然後賦值給bCheap, // bCheap的值也為false,表示a並不比b便宜 bool bCheap = a < b;
在C++中,我們通常是用關系操作符來判斷某個條件是否成立,然後配合稍後我們將要學到的條件結構,決定代碼的執行路徑,對數據進行不同的處理以獲得不同的執行結果。比如,我們要表示網吧禁止未滿十八歲的未成年人進入:
int nAge = 0; cout<<"請輸入你的年齡:"; // 用戶輸入年齡 cin>>nAge; // 用關系操作符“>=”判斷輸入的年齡是否大於等於18 // 判斷是否成年人 if(nAge >= 18) { // nAge的值大於等於18 cout<<"歡迎來到紅樹林網吧"<<endl; } else // nAge的值小於18 { cout<<"很抱歉,未成年人不能進入"<<endl; }
在這段代碼中,我們首先讓用戶輸入年齡並將其保存到nAge變量中,然後在if條件結構中,用“>=”關系操作符將其與18進行大小比較。如果nAge的值大於等於18,則“nAge >= 18”表達式的值為true,表示是成年人,代碼會進入if後面的語句執行,輸出歡迎信息。反之,則表示是未成年人,代碼會進入else後面的代碼執行,將其拒之門外。
最佳實踐:不要使用“==”判斷兩個浮點數是否相等
考慮下面這段代碼的輸出是什麼:
float a = 0.1; if(a*10 == 1.0) { cout<<"0.1*10等於1.0"<<endl; } else { cout<<"0.1*10不等於1.0"<<endl; }
如果我要是告訴你這段代碼的輸出是“0.1*10不等於1.0”會不會讓你大跌眼鏡呢?雖然有點意外,可這就是事實。這是因為在C++中,除了某些特殊的浮點數(比如,0.5、0.25等)之外,我們無法精確地表達大多數浮點數,所以比較兩個浮點數是否相等是不保險的。雖然在表面上看來,a的值為0.1,a*10等於1.0是確定無疑的,但是因為浮點數a無法精確地表達0.1這個值,存在一個十分微小的誤差,當經過一定的運算後,這個誤差會累積到一個可被察覺的程度,這時再用“==”將其與它的理論結果進行比較時,其結果有可能是相等,也有可能是不相等。而至於到底是哪一個,則取決於計算機硬件和編譯器優化設置。這段代碼在某台計算機上輸出的結果可能是“0.1*10不等於1.0”,但是在另外一台計算機上輸出的結果卻又可能是“0.1*10等於1.0”。所以,為了保證代碼行為的一致性,不要在代碼中使用“==”比較兩個浮點數是否相等。
如果確實需要在代碼中比較兩個浮點數是否相等又該怎麼辦呢?最簡單的方法就是設定一個允許的誤差值(根據我們的精度要求而定),當兩個浮點數之差的絕對值在這個誤差范圍內時,就認為兩個浮點數相等,反之則認為兩個浮點數不相等。例如,上面的代碼可以修改為下面的樣子,以保證代碼行為的一致。
float a = 0.1; // 相等的誤差范圍 const float fEpsilon = 0.0001; // 判斷兩個浮點數之差的絕對值(用fabs()函數取得)是否在誤差范圍內 // 如果在,則認為兩個浮點數相等 if(fabs(a*10 - 1.0) < fEpsilon) { cout<<"0.1*10等於1.0"<<endl; } else // 反之,則認為兩個浮點數不相等 { cout<<"0.1*10不等於1.0"<<endl; }
經過這樣的改寫,這段浮點數相等比較的代碼在任何計算機上都可以得出一致的正確的結果。
在處理復雜事務時,我們往往需要根據多個條件來決定是否執行某項操作。例如,網吧門口貼著這樣一張條子:
“只有年滿十八歲而且兜裡有錢的人才可以進入網吧。”
這裡的“而且”,實際上就是對“年滿十八歲”和“兜裡有錢”這兩個條件進行“與”的邏輯運算,只有這兩個條件同時滿足時,才算是符合條件,才能夠執行“進入網吧”的操作。像“而且”這種對兩個條件進行邏輯運算的動詞,在C++中,我們用邏輯操作符來表達。
C++提供的邏輯操作符包括以下三種。
l !(非):計算操作數的邏輯非。
l &&(與):計算兩個操作數的邏輯與。
l ||(或):計算兩個操作數的邏輯或。
其中,“!”是一元操作符,只能放在單個bool類型的操作數之前,對其進行取非操作,獲得與之相反的邏輯值。例如:
bool bFlag = true; // 定義一個bool類型的變量bFlag並賦值為true // 對操作數bFlag進行取非操作,整個表達式的結果為false, // 與操作數bFlag的值相反 !bFlag;
“&&”和“||”都是二元操作符,它們連接兩個bool類型的操作數,並對其進行邏輯運算,所獲得的bool類型的結果值作為整個表達式的值。“&&”的作用是計算兩個操作數的邏輯與,也就是只有當兩個操作數的值都為true時,整個表達式的值才為true,否則為false。“||”的作用是計算兩個操作數的邏輯或,只要兩個操作數中有一個為true,整個表達式的值就為true,否則為false。
在具體的編程實踐中,邏輯操作符常常和關系操作符配合使用,在條件結構中被用來表達比較復雜的條件邏輯判斷。例如,我們要根據學生的語文和數學成績綜合評定學生的考核等級,規則是:如果語文和數學成績都是60分以上,則為“合格”;在“合格”的基礎上,只要其中有一門成績是85分以上,就是“優秀”。在C++中,我們可以這樣來表達這個復雜的邏輯判斷:
cout<<"請輸入學生的語文、數學成績(例如,82 96):"; // 定義變量保存輸入的數據 int nChs = 0; int nMath = 0; // 輸入數據,並保存到變量 cin>>nChs>>nMath; // 對變量進行邏輯判斷評定等級 // 首先判斷兩個分數是否都在60分以上,達到“合格”的標准 if((nChs >= 60)&&(nMath >= 60)) { // 在“合格”的基礎上,判斷是否有一門的成績在85以上,達到“優秀”的標准 if((nChs >= 85)||(nMath >= 85)) { cout<<"優秀"<<endl; } else // 如果沒有達到“優秀”的標准,那就是“合格” { cout<<"合格"<<endl; } } else // 如果沒有達到“合格”標准,那就是“不合格” { cout<<"不合格"<<endl; }
在這裡,我們用“&&”操作符對“nChs >= 60”和“nMath >= 60”進行了“與”運算,也就是這兩個表達式的值同時為true時,最終結果才為true。換句話說,要想讓最終結果為true,也就是達到“及格”標准,那就必然要求“nChs >= 60”和“nMath >= 60”這兩個表達式的值同時為true,而要想讓這兩個表達式的值同時為true,自然就要求nChs和nMath的值要同時大於60,這樣就表達了“語文和數學成績都是60分以上”的“合格”標准的邏輯判斷。只有滿足這個邏輯判斷,達到合格標准,才能進入下一級判斷是否“優秀”;否則,只要nChs和nMath中的任意一個小於60,就無法滿足這個邏輯判斷,程序就會進入else分支輸出“不合格”的提示。在用“||”操作符對“優秀”條件進行判斷時,只要“nChs >= 85”和“nMath >= 85”這兩個表達中的任意一個為true,最終結果就為true。換句話說,也就是只要nChs和nMath中任意一個大於85,最終結果就為true,這也就表達了“只要其中有一門成績是85分以上”的“優秀”標准。
最佳實踐:注意“&&”和“||”操作符的“邏輯短路”機制
這兩個操作符是用來對多個表達式進行邏輯運算,取得多個表達式經過運算後的最終結果。在進行運算時,如果憑借前面部分表達式的值就已經可以確定整個表達式的最終結果,C++將不再繼續對後面剩下的表達式進行運算,而是直接抄近路返回已經得到的整個表達式的值。這種機制被稱為“邏輯短路”,也就是走了捷徑。
這樣的解釋還是有點抽象,我們就以上面的例子為例,看看“短路機制”到底是怎麼“抄近路”的。
if((nChs >= 60)&&(nMath >= 60))
在這個條件判斷中,我們用“&&”操作符對“nChs >= 60”和“nMath >= 60”進行“與”運算,如果nChs的值小於60,也就是第一個條件表達式“nChs >= 60”的值為false時,無論後面的“nMath >= 60”表達式的值為true或者false,我們都已經能夠確定整個表達式的值為false,C++將用false作為整個表達式的值,跳過對後面“nMath >= 60” 的判斷而直接結束對整個表達式的計算。所以,為了減少計算提高效率,C++就直接跳過對第二個關系表達式的計算,抄了近路。
在使用“||”進行“或”運算時,也同樣存在這種“邏輯短路”。例如:
if((nChs >= 85)||(nMath >= 85))
在這個用“||”操作符表達的“或”邏輯運算中,如果nChs的值大於等於85,則第一個關系表達式“nChs >= 85”的值為true,進而可以確定整個表達式的值為true,C++同樣也會跳過對第二個關系表達式“nMath >= 85”的計算,而用true作為整個表達式的值,直接結束對整個表達式的計算。
除了減少不必要的計算,在一定程度上提高效率之外,“邏輯短路”更大的意義在於,在某些情況(後一個條件判斷以前一個條件成立為前提)下,它可以減少邏輯判斷的層次。比如,如果我們要以某個結構體指針的成員變量作為條件,得先判斷這個結構體指針是否有效,然後才能對指針的成員變量進行判斷。如果沒有“邏輯短路”機制,我們的代碼要寫成:
Human* p = nullptr; // … if(nullptr != p)// 先判斷指針是否有效 { // 然後再判斷指針的成員變量是否符合條件 // 第二個條件判斷以第一個條件成立為前提 if(p->m_nAge >= 18) { // ... } }
而如果利用“邏輯短路”機制,這種條件判斷就可以簡化為:
Human* p = nullptr; // … if((nullptr != p) && (p->m_nAge >= 18)) { // ... }
這樣在進行條件判斷時,同樣會先判斷指針p是否為空指針,如果第一個條件滿足,才會繼續向下判斷它的成員變量是否滿足條件。實現相同的條件判斷,但是卻減少了邏輯判斷的層次,簡化了代碼,而這才正是“邏輯短路”機制的主要運用場景。
二元。。
下面是逗號運算符的講解:
C語言提供一種特殊的運算符——逗號運算符。用它將兩個表達式連接起來。如:
3+5,6+8
稱為逗號表達式,又稱為“順序求值運算符”。逗號表達式的一般形式為
表達式1,表達式2
逗號表達式的求解過程是:先求解表達式1,再求解表達式2。整個逗號表達式的值是表達式2的值。例如,上面的逗號表達式“3+5,6+8”的值為14。又如,逗號表達式
a=3*5,a*4
對此表達式的求解,讀者可能會有兩種不同的理解:一種認為“3*5,a*4” 是一個逗號表達式,先求出此逗號表達式的值, 如果a的原值為3,則逗號表達式的值為12,將12賦給a, 因此最後a的值為12。另一種認為:“a=3*5”是一個賦值表達式”,“a*4”是另一個表達式,二者用逗號相連,構成一個逗號表達式。這兩者哪一個對呢?賦值運算符的優先級別高於逗號運算符, 因此應先求解a=3*5(也就是把“a=3*5”作為一個表達式)。經計算和賦值後得到a的值為15,然後求解a*4,得60。整個逗號表達式的值為60。
一個逗號表達式又可以與另一個表達式組成一個新的逗號表達式,如(a=3*5,a*4),a+5 先計算出a的值等於15,再進行a*4的運算得60(但a值未變,仍為15),再進行a+5得20,即整個表達式的值為20。
逗號表達式的一般形式可以擴展為
表達式1,表達式2,表達式3……表達式n
它的值為表達式n的值。
逗號運算符是所有運算符中級別最低的。因此,下面兩個表達式的作用是不同的:
① x=(a=3,6*3)
② x=a=3,6*a
第①個是一個賦值表達式,將一個逗號表達式的值賦給x,x的值等於18。第②個是逗號表達式,它包括一個賦值表達式和一個算術表達式,x的值為3。
其實,逗號表達式無非是把若干個表達式“串聯”起來。在許多情況下,使用逗號表達式的目的只是想分別得到各個表達式的值,而並非一定需要得到和使用整個逗號表達式的值,逗號表達式最常用於循環語句(for語句)中.
請注意並不是任何地方出現的逗號都是作為逗號運算符。例如函數參數也是用逗號來間隔的。如
printf("%d,%d,%d",a,b,c);
上一行中的“a,b,c”並不是一個逗號表達式,它是printf函數的3個參數,參數間用逗號間隔。
如果改寫為
printf("%d,%d,%d",(a,b,c),b,c);
則“(a,b,c)”是一個逗號表達式,它的值等於c的值。括弧內的逗號不是參數間的分隔符而是逗號運算符。括弧中的內容是一個整體,作為printf函數的一個參數。
C語言表達能力強,其中一個重要方面就在於它的表達式類型豐富,運算符功能強,因而c使用靈活,適應性強
參考資料: hi.baidu.com/...3.html...余下全文>>
邏輯非運算符“!”的結合方法是自右向左優先級為2,和不等於運算符“!=”不同,“!=”的結合方法是自左向右優先級比<=低
“!”是單目運算符,優先級必然高於雙目運算符,像“!=”,“<=”等