float a; long b; 直接書寫變量名也是一條語句,其導致編譯器生成一條讀取相應變量的內容的語句。即可以如下書寫:
a;
上面將生成一條讀取內存的語句,即使從內存中讀出來的數字沒有任何應用(當然,假如編譯器開了優(yōu)化選項,則上面的語句將不會生成任何代碼)。從這一點以及上面的c = a / b * 120.4f;語句中,都可以看出一點——變量是可以返回數字的。而變量返回的數字就是按照變量的類型來解釋變量對應內存中的內容所得到的數字。這句話也許不是那么輕易理解,在看過后面的類型轉換一節(jié)后應該就可以理解了。 因此為了將數據寫入一塊內存,使用賦值語句(即等號);要讀取一塊內存,書寫標識內存的變量名。所以就可以這樣書寫:a = a + 3; 假設a原來的值為1,則上面的賦值語句將a的值取出來,加上3,得到結果4,將4再寫入a中去。由于C++使用“=”來代表賦值語句,很輕易使人和數學中的等號混淆起來,這點應注重。
而如上的float a;語句,當還未對變量進行任何賦值操作時,a的值是什么?上帝才知道。當時的a的內容是什么(對于VC編譯器,在開啟了調試選項時,將會用0xCCCCCCCC填充這些未初始化內存),就用IEEE的real*4格式來解釋它并得到相應的一個數字,也就是a的值。因此應在變量定義的時候就進行賦值(但是會有性能上的影響,不過很小),以初始化變量而防止出現莫名其妙的值,如:float a = 0.0f;。
賦值操作符
上面的a = a + 3;的意思就是讓a的值增加3。在C++中,對于這種情況給出了一種簡寫方案,即前面的語句可以寫成:a += 3;。應當注重這兩條語句從邏輯上講都是使變量a的值增3,但是它們實際是有區(qū)別的,后者可以被編譯成優(yōu)化的代碼,因為其意思是使某一塊內存的值增加一定數量,而前者是將一個數字寫入到某塊內存中。所以假如可能,應盡量使用后者,即a += 3;。這種語句可以讓編譯器進行一定的優(yōu)化(但由于現在的編譯器都非常智能,能夠發(fā)現a = a + 3;是對一塊內存的增值操作而不是一塊內存的賦值操作,因此上面兩條語句實際上可以認為完全相同,僅僅只具有簡寫的功能了)。
對于上面的情況,也可以應用在減法、乘法等二元非邏輯操作符(不是邏輯值操作符,即不能a &&= 3;)上,如:a *= 3; a -= 4; a = 34; a >>= 3;等。
除了上面的簡寫外,C++還提供了一種簡寫方式,即a++;,其邏輯上等同于a += 1;。同上,在電腦編程中,加一和減一是經常用到的,因此CPU專門提供了兩條指令來進行加一和減一操作(轉成匯編語言就是Inc和Dec),但速度比直接通過加法或減法指令來執(zhí)行要快得多。為此C++中也就提供了“++”和“—”操作符來對應Inc和Dec。所以a++;雖然邏輯上和a = a + 1;等效,實際由于編譯器可能做出的優(yōu)化處理而不同,但還是如上,由于編譯器的智能化,其是有可能看出a = a + 1;可以編譯成Inc指令進而即使沒有使用a++;卻也依然可以得到優(yōu)化的代碼,這樣a++;將只剩下簡寫的意義而已。
a = 0; b = 1; ( a *= 2 ) && ( b += ++a ); 按照優(yōu)先級的順序,編譯器發(fā)現要先計算a *= 2,再計算++a,接著“+=”,最后計算“&&”。然后編譯器發(fā)現這個計算過程中,出現了“&&”左邊的數字這個序列點,其要保證被優(yōu)先計算,這樣就有可能不用計算b += ++a了。所以編譯器先計算“&&”的數字,通過上面的計算過程,編譯器發(fā)現就要計算a *= 2才能得到“&&”左邊的數字,因此將先計算a *= 2,返回a的地址,然后計算“&&”左邊的數字,得a的值為0,因此就不計算b += ++a了。而不是最開始想象的由于優(yōu)先級的關系先將a加一后再進行a的計算,以返回1。所以上面計算完畢后,a為0,b為1,返回0,表示邏輯假。
因此序列點的出現是為了保證一些非凡規(guī)則的出現,如上面的“&&”和“”。再考慮“,”操作符,其操作是計算兩邊的值,然后返回右邊的數字,即:a, b + 3將返回b + 3的值,但是a依舊會被計算。由于“,”的優(yōu)先級是最低的(但高于前面提到的“數字”操作符),因此假如a = 3, 4;,那么a將為3而不是4,因為先計算“=”,返回a的地址后再計算“,”。又:
a = 1; b = 0; b = ( a += 2 ) + ( ( a *= 2, b = a - 1 ) && ( c = a ) ); 由于“&&”左邊數字是一個序列點,因此先計算a *= 2, b的值,但根據“,”的返回值定義,其只返回右邊的數字,因此不計算a *= 2而直接計算b = a – 1得0,“&&”就返回了,但是a *= 2就沒有被計算而導致a的值依舊為1,這違反了“,”的定義。為了消除這一點(當然可能還有其他應用“,”的情況),C++也將“,”的左邊數字定為了序列點,即一定會優(yōu)先執(zhí)行“,”左邊的數字以保證“,”的定義——計算兩邊的數字。所以上面就由于“,”左邊數字這個序列點而導致a *= 2被優(yōu)先執(zhí)行,并導致b為1,因此由于“&&”是序列點且其左邊數字非零而必須計算完右邊數字后才恢復正常優(yōu)先級,而計算c = a,得2,最后才恢復正常優(yōu)先級順序,執(zhí)行a += 2和“+”。結果就a為4,c為2,b為5。
注重前面之所以會朝好的方向發(fā)展(即char轉成float),完全是因為“==”的緣故,其要求這么做。下面考慮“=”:short b = 3543; char a = b;。因為b的值是short類型,而“=”的要求就是一定要將“=”右邊的數字轉成和左邊一樣,這樣才能進行正確的內存的寫入(簡單地將右邊數字返回的二進制數復制到左邊的地址所表示的內存中)。因此a將為-41。但是上面是編譯器按照“=”的要求自行進行了隱式轉換,可能是由于程序員的疏忽而沒有發(fā)現這個錯誤(以為b的值一定在-128到127的范圍內),因此編譯器將對上面的情況給出一個警告,說b的值可能被截斷。為了消除編譯器的疑慮,如下:char a = ( char )b;。這樣稱為顯示類型轉換,其告訴編譯器——“我知道可能發(fā)生數據截斷,但是我保證不會截斷”。因此編譯器將不再發(fā)出警告。但是如下:char a = ( char )3543;,由于編譯器可以肯定3543一定會被截斷而導致錯誤的返回值,因此編譯器將給出警告,說明3543將被截斷,而不管前面的類型轉換操作符是否存在。