1. 程式人生 > >C++ Primer Plus--資料處理(三)

C++ Primer Plus--資料處理(三)

介紹C++變數的命名規則,以及資料型別

3.1 簡單變數

int a;
a = 5;

上述語句告訴程式,它正在儲存整數,並使用名稱a來表示該整數的值。實際上,程式將找到一塊能夠儲存整數的記憶體,將該記憶體單元標記為a,並將5複製到該記憶體單元中,然後,可以在程式中使用a來訪問該記憶體單元。可以通過&運算子來檢索a的記憶體地址。

變數名

C++提倡使用有一定含義的變數名,如果表示差旅費,應將其命名為cost_of_trip或costOfTrip,而不要命名為a或x等。必須遵循幾種簡單的C++命名規則:

  • 在名稱中只能使用字母字元、數字和下劃線(_);
  • 名稱的第一個字元不能是數字;
  • 區分大小寫
  • 不能將C++關鍵字用作名稱
  • 以兩個下劃線或下劃線和大寫字母開頭的名稱被保留給實現(編譯器及其使用的資源)使用,以一個下劃線開頭的名稱被保留給實現,用作全域性識別符號;
  • C++對於名稱的長度無限制,名稱中所有的字元都有意義。

最後兩點和前幾點不同,因為使用_time_stop或_Donut或__time這樣的名稱不會導致編譯器錯誤,而會導致不確定性。即,不知道結果將是什麼,不出現編譯錯誤的原因是,這樣的名稱不是非法的,但是要保留給現實使用。全域性名稱指的是名稱被宣告的位置。

命名方案

在給變數命名時,可能在變數名中加入其他資訊,即描述變數型別或內容的字首,例如,可以將整型變數命名myWeight命名為intMyWeight或nMyWeight,這樣,在閱讀程式碼時,一目瞭然,每個變數的意義和型別。常以這種方式使用的字首用:str或sz(表示以空字元結束的字串)、b(表示布林值)、p (表示指標)、c(表示單個字元)。

整型

整型是沒有小數部分的數字,比如:98,-86,0。C++提供了好幾種整型來儲存整數。使用的記憶體越大,可以表示的整數值範圍也越大。另外,有的型別(符號型別)可以表示正值和負值,而有的型別(無符號型別)不能表示負值。術語寬度使用者描述儲存整數時使用的記憶體量,使用的記憶體越多,則越寬。C++的基本整型(按照寬度遞增的順序排序):

  • char:該型別有一些特殊屬性,常用來表示字元,而不是數字
  • short
  • int
  • long
  • long long

計算機記憶體由一些叫做位(bit)的單元組成。C++中short、int、long和long long型別都是通過使用不同數目的位來儲存值。

在不同的系統中,每種的型別的寬度都不一樣,C++提供了一種靈活的標準,確保了最小長度(從C語言借鑑而來),如下:

  • short至少16位
  • int至少與short一樣長
  • long至少32位,且至少與int一樣長
  • long long至少64位,且至少與long一樣長

實際上short是short int的簡稱,long是long int的簡稱。

位與位元組

計算機記憶體的基本單元時位(bit),位元組(byte)通常指的是8位的記憶體單元。從這個意義上說,位元組指的就是描述計算機記憶體量的度量單位,1KB等於1024位元組,1MB等於1024KB。

8位的記憶體塊可以設定出256種不同的組合,因為每一位由兩種設定(0和1),所以8位的總組合位:2^8-256。因此8位單元可以表示0-255或-128到127。每增加一位,組合數變加倍。

C++中對位元組的定義與此不同,C++位元組由至少能夠容納實現的基本字符集的相鄰位組成,也就是說可能取值的數目必須等於或超過字元數目。在美國,基本字符集通過是ASCII和EBCDIC集,它們都可以用8位來容納,所以在使用這兩種字符集的系統中,C++通常包括8位。然而,國際程式設計可能需要使用更大的字符集,如Uunicode,因此有些實現可能使用16位甚至32位的字元。

C++中檢測型別的長度,sizeof運算子返回型別或變數的長度,單位為位元組。 比如:sizeof(int),或者 對一個變數利用sizeof,如:sizeof n_short (short n_short;)。

在標頭檔案climits(舊版本limits.h)中包含了關於整型限制的資訊。具體地說,它定義了表示各種顯示的符合名稱。比如:INT_MAX 為int的最大值,CHAR_BIT為位元組的位數。 SHRT_MAX為short的最大值,LLONG_MAX為long long的最大值,LONG_MAX為long的最大值。INT_MIN為int的最小值。這裡只給出了部分,詳細的可以檢視書籍P41。

* 符號常量–預處理方式*

climits檔案中包含於下面類似的語句行:

#define INT_MAX 32767

在C++編譯過程中,首先將原始碼傳給前處理器。在這裡,#define和#include一樣,也是一個預處理編譯指令。該編譯指令告訴前處理器:在程式中查詢INT_MAX,並將所有的INT_MAX替換為32767。

初始化

初始化將複製和宣告合併在一起。如下:

int n_int = INT_MAX;

可以將變數初始化為另一個變數,條件是後者是已經定義過,甚至可以使用表示式來初始化變數,條件是當程式執行到該宣告時,表示式中的所有值都已經定義過:

int uncles = 5;
int aunts = uncles;
int chairs = aunts + uncles + 4;

前面的初始化繼承自C語言,C++還有另一個C語言沒有的初始化語法:

int wrens(432);

注意: 如果不對函式內部定義的變數進行初始化,該變數的值將是不確定的。這意味著該變數的值將是它被建立之前,相應記憶體單元儲存的值。

在變數宣告時對它進行初始化,可避免以後忘記給它賦值的情況發生。

C++11初始化方式

另一種初始化方式,這種方式使用者陣列和結構,但在C++98中,也可以用於單值變數:

int hamburgers = {24};

將大括號初始化器用於單值變數的情形不多,但在C++標準使得這種情形更多了。首先,採用這種方式時,可以使用等號(=),也可以不使用:

int emus{7};
int rheas = {12}

其次,大括號可以為空,即變數被初始化為零:

int rocs = {};
int psychis{};

無符號型別

前面介紹的4種整數都有一種不能儲存負數值的無符號變體。其優點是可以增大變數能夠儲存的最大值。例如:如果short表示的範圍為-32768到32767,則無符號版本的表示範圍為0-65535。當然,僅當數值不會為負時才應該使用無符號型別,如人口、件數等。建立無符號版本的基本整性,只需要使用關鍵詞unsigned來修改:

unsigned short change;
unsigned long long lang_lang;

注意:unsigned 本身是 unsigned int 的縮寫。

對於一個short變數,取short的最大值(32767),對於符號整型,對最大值加一,則該值將變為-32768。對於無符號整型,對最大值加一,則該值將變為:32768。如果將無符號整數0,減一則該值會變成65535。

可以看出,這些整型變數的行為就像里程錶,如果超越了限制,其值將為範圍另一端的取值。

選擇整型型別

int被設定為對目標計算機而言最為“自然”的長度。自然長度指的是計算機處理起來效率最高的長度。如果沒有其他要求,則應該使用int。

如果知道變數可能表示的整數值大於16位整數的最大可能值,則使用32位。即使系統上int為32位,也應該這樣。如果儲存的值大於20億,可使用long long。

如果short比int小,則使用short可以節省記憶體。通常,僅當有大型整型陣列時,才有必要使用short。如果節省記憶體很重要,則應使用short而不是int,即使它們的長度一樣。例如,如果將程式從int為16位的系統移植到int為32的系統,則使用者儲存int陣列的記憶體量將加倍,但short不會受影響。

如果只需要使用一個位元組,可使用char。

整型字面量

整型字面量(常量)是顯式地書寫常量,如212或1776。C++能夠以三種不同的計數方式來書寫整型:基數為10、8和16。如果第一位為1~9,則基數為10(十進位制);如果第一位為0,第二位1~7,則基數為8(八進位制);如果前兩位為0X或0x,則基數為16(十六進位制)。具體進位制問題:

在預設情況下,cout以十進位制格式顯示整數,而不管這些整數在程式中如何書寫,如:

int waits = 0X42;
cout << waits; 
//輸出結果為66

這些表達方式僅僅是為了表達上的方便。例如:如果CGA視訊記憶體段為十六進位制B000,則不必在程式中使用之前將它轉換為十進位制45056,而只需使用0xB000。但是不管把值書寫為10、012還是0xA,都將以相同的方式儲存在計算機中—被儲存為二進位制。

如果要以十六進位制或八進位制方式顯示值,則可以使用cout的一些特殊特性。標頭檔案iostream提供了控制符endl,用於指示cout重起一行。同樣,它還提供了控制符dec、hex和oct,分別表示用於指示cout以十進位制、十六進位制和八進位制格式顯示整數。

using namespace std;
int chest = 42;
cout << hex;
cout << chest; //輸出2a
cout << oct;
cout << chest; //輸出52

諸如cout<

C++如何確定常量的型別

程式的宣告中將特定的整型變數的型別告訴了C++編譯器,但編譯器是如何確定常量的型別呢?假設在程式中使用一個常量表示一個數字:

cout << "Year = " << 2018 << endl;

程式將1492儲存為int、long還是其他型別呢?答案是,除非有理由儲存為其他型別(如使用了特殊的字尾來表示特定的型別,或者值太大,不能儲存為int),否則C++將整型儲存為int。

關於字尾,字尾是放在常量後面的字母,用於表示型別。具體如下:

字尾 型別
l或L long常量
ul unsigned long常量
ll或LL long long常量
LU或UL ungigned long常量
ull或Ull或uLL或ULL unsigned long long 常量

對於長度,C++中不同進位制,採用儲存整型的規則不同:

進位制 儲存型別
十進位制 使用後面能過儲存該數的最小型別來表示: int、long 、long long
八進位制和十六進位制 使用後面幾種型別中能過儲存該數的最小型別來表示: int、unsigned int、long、unsigned long、long long 、unsigned long long

十六進位制數0x9C40(40000)將被表示為unsigned int,這是因為十六進位制常用來表示記憶體地址,而記憶體地址沒有符號。

char型別:字元和小整數

char型別是專門為儲存字元而設計的。程式語言通過使用字母的數值編碼來儲存字元。因此char型別是另一種整型。它足夠長,能過表示計算機系統中的所有字元—字母、數字、標點符號等等。實際上,很多系統支援的字元不操作128個,因此用一個位元組就可以表示所有的符號。雖然,char最常用來處理字元,但也可以將它用做比short更小的整型。

在美國,最常用的符號集是ASCII字符集。例如,字元A的編碼是65,字母M的編碼為77。

char ch;
cout << "Enter a chararcter: " << endl;
cin >> ch;
cout << "Thank you for the " << ch << " character" << endl;

輸入M,結果輸出為 Thank you for the M character.程式列印的是M,而不是77。通過檢視記憶體可知,77是儲存在變數ch中的值。這種神奇的力量來自cin和cout,而不是char,這些工具完成了轉換工作。輸入時,cin將鍵盤輸入的M轉換為77;輸出是cout將77轉換為M。

如果將77儲存在int變數中,則cout將把它顯示為77(也就是說cout顯示兩個字元7)。C++中書寫字元字面量:將字元用單引號括起來。如’M’。(注意:C++對字元用單引號對字串使用雙引號,這兩者差別比較大)。cout引入一項新特性–cout.put()函式,該函式顯示一個字元。

char ch = 'M'; 
int i = ch;
cout << i; //結果為77
ch = ch + 1;
i = ch;
cout << i //結果為78
cout.put(ch); //結果為N

上面程式中,ch儲存的實際上是整數,因此可以對其使用整數操作,如加一。

char ch;
cin >> ch; //輸入5,實際上ch儲存的整數53(5的ASCII編碼)

上述程式碼將讀取字元“5”.並將其對應的字元編碼儲存到ch變數中。

int n;
cin >> n; //輸入5

上述程式碼獲取字元“5”,並將其轉換為對應的數字5,並存儲到n變數中。

成員函式cout.put()

cout.put()成員函式提供了一種顯示字元的方法,可以代替<<運算子。cout.put存在的必要性:在c++的Release2.0之前,cout將字元變數顯示為字元,而將字元常量(如‘M’和‘N’)顯示為數字。問題是,C++早期版本將字元常量儲存為int型別。也就是說,‘M’的編碼將被儲存在一個16或32為單元中。而char變數一般佔8位。下面的語句從常量“M”中賦值左邊的8位到變ch中:

char ch = 'M'

遺憾的是,對於cout來說,‘M’和ch看上去有天壤之別,雖然儲存的值一樣。下面的語句將列印字元M的ASCII碼,而不是字元M:

cout << 'M';

但是下面列印字元M:

cout.put('M');

C++Release2.0之後,將字元常量儲存位char型別,而不是int,意味著cout現在可以正確處理字元常量。

char字面值

在C++中,書寫字元常量的方式有多種,對於常規字元(字母,數字和標點符號),最簡單的方法是將字元用單引號括起來。這種表示法代表的是字元的數字編碼。例如,ASCII系統中的對應情況如下:

  • ‘A’為65,即字元A的ASCII
  • ‘a’為97
  • ‘0’為48
  • ’ ‘為32
  • ‘!’為33

這種表示法優於數值編碼,更加清晰,而不需要知道編碼方。如果系統使用的是EBCDIC,則A的編碼值不是65,但’A‘表示的仍然是字元A。

轉義字元: 有些字元不能之間通過鍵盤輸入到程式中,比如:回車等。C++提供了一種特殊的表示方法–轉義字元。例如:\”將雙引號作為常規字元,\t表示水平製表符等等。

將轉移字元作為字元常量時,應用單引號括起來;將他放在字串中不要使用單引號。

基於字元八進位制和十六進位制編碼來使用轉義字元。例如,Ctrl+Z的ASCII碼為26,對應的八進位制編碼為032,十六進位制編碼為0X1a。可以使用下面的轉義字元來表示該字元: \032或\x1a。

通用字元名

通用字元名的用法類似於轉義序列。通用字元名可以以\u或\U開頭。\u後面是8個十六進位制位,\U後面則是16個八進位制位。這些位表示的是字元的ISO 10646碼點。(ISO 10646是一種正在制定的國際標準,為大量的字元提供了數字編碼)。

#include <iostream>
using namespace std;

int main()
{
    cout << "Let them eat g\u00E2teau.\n";
    return 0;
}

輸出為:Let them eat gâteau.

Unicode提供了一種表示各種字符集的解決方案—為大量字元和符號提供標準數值編碼,並根據型別給他們分類。例如:ASCII碼為Unicode的子集,因此在這兩種系統中,美國的拉丁字母(如A和Z)表示相同。然後,Unicode還包含其他拉丁字元。

Unicode給每個字元制定一個編號–碼點。Unicode碼點通常類似於:U-222B。其中U表示這是一個Unicode字元,而222B是該字元的十六進位制編碼。

國際標準組織(ISO)建立了一個工作組,專門開發ISO 10646—這也是對一個多語言文字進行編碼的標準。ISO 10646小組和Unicode小組從1991年開始合租,以確保他們的同步。

signed 和 unsigned char

與int不同的是,char預設情況下既不是沒有符號,也不是有符號。是否有符號由C++決定。

char fodo //可能為有符號,也可能無符號
unsigned char bar //無符號
signed char snark //有符號

如果char作為數值型別,unsigned char型別表示的範圍為0~255,而signed char表示的範圍為-128~127。如果使用char變數儲存200這樣大的值,在某些系統上可以,而在另一些系統上可能不可以。但是使用unsigned char可以在任何系統上達到這種目的。如果使用char儲存ASCII字元,則有沒有字元都沒關係。

wchar_t

wchar_t(寬字元型別)可以表示擴充套件字符集。wchar_t型別是一種整數型別,它有足夠的空間,可以表示系統使用的最大擴充套件字符集。這種型別與另一種整型(底層(underlying)型別)的長度和字元屬性相同,對底層系統的選擇取決於實現,因此在一個系統中,他可能是unsiged short,而在另一個系統中,則可能是int。

cin和cout將輸入和輸出看作是char流,因此不適於用來處理wchar_t型別。iostream標頭檔案的最新版提供了類似的工具—wcin和wcout,可用於處理wchar_t。另外,可以通過加上字首L來表示寬字元常量和寬字串。

wchar_t bob = L'P';
wcout << L"tall" << bob << endl;

本書不使用寬字元型別,但應知道這種型別,尤其是在進行國際程式設計或使用Unicode或ISO 10646時。

C++11新增型別 char16_t和char32_t

隨著程式設計人員的日益熟悉Unicode,型別wchar_t顯然不再滿足需求。事實上,在計算機系統上驚喜字元和字串編碼時,僅使用Unicode碼點並不夠。具體說,進行字串編碼時,如果由特定長度和符號特徵的型別,將很有幫助,而型別wchar_t的長度和符號特徵隨實現而已。因此,C++11新增了char16_t和char32_t,兩者都無符號,前者長為16為,後者長為32為。使用字首u表示char16_t字元常量和字串常量,如u’be goog’;使用字首U表示char32_t常量,如:U’R’。型別char16_t與/u00F6形式的通用字元名匹配,而型別char32_t與/U0000222B形式的通用字元名匹配。

char16_t ch1 = u'q';
char32_t ch2 = U'\U0000222B';

bool型別

在計算中,布林變數的值可以時true或false。

bool is_ready = true;

字面值true和false都可以通過提升轉換為int型別,true被轉換為1,false被轉換為0。任何非零值可以轉換為true,而零被轉換為false。

bool start = -100;
bool end = 0;

3.2 const限定符

使用關鍵字const來修改變數宣告和初始化,例如,假設需要一個表示一年中月份數的符號常量:

const int Months =12;

這樣,便可以在程式中使用Months,而不是12了。常量(如Months)被初始化後,其值就被固定了,編譯器將不允許在修改該常量的值。如果這樣做,g++將指出程式檢視給一個只讀變數賦值。const被叫做限定符,因為它限定了宣告的含義。

一種常見的做法是將名稱的首字母大寫,以提醒Months是一個常量。

const比#define好,首先,它能明確指定型別;其次,可以使用C++的作用域規則將定義限制在特定的函式或檔案中;最後,const使用者更復雜的型別,後面介紹。

建議使用const而不是#define。

3.3 浮點數

對於浮點數,計算機將其分為兩部分儲存。一部分表示值,另一部分用於對值進行放大或縮小。比如:對於數字34.1245和34124.5,它們處理小數點位置不同外,其他都相同。可以把第一個數表示為0.341245(基準值)和100(縮放因子),而將第二個表表示為0.341245(相同基準值)和100(縮放因子更大)。縮放因子的作用是移動小數點的位置,術語浮點因此而得名。C++內部表示浮點數的方法與此相同,只不過是二進位制,因此縮放因子是2的冪,不是10的冪。

3.3.1 書寫浮點數

C++有兩種書寫浮點數的方式:

  • 第一種使用常用的標準小數點表示法:12.34
  • 第二種E表示法: 3.45E6指的是3.45與1000000相乘的結果;8.33E-4, 7E5,-8.33E4。

E表示法適合表示非常大和非常小的數。E表示法確保數字以浮點數格式儲存,即使沒有小數點。注意,既可以使用E也可以使用e,指數可以是正數也可使負數。數字中不能有空格,比如:7.2 E6是非法的。

指數為負數意味著除以10的乘方,而不是乘以10的乘方。因此8.33E-4表示8.33/104,即0.000833。數值前面的符號用於數值,而指數的符號使用者縮放。

3.3.2 浮點型別

C++三種浮點型別:float、double和long double.這些型別是按照它們可以表示的有效位數和允許的指數最小範圍來描述的。有效位數是數字中有意義的位。例如:14179英尺,使用了5個有效位;13000英尺,使用了2個有效位,其他3位為佔位符;1.234,使用了4個有效位。

C++對有效位數的要求是,float至少32位;double至少48位,且不少於float;long double至少跟double一樣多。通常,float位32位,double為64位,long double為80、96或128位。另外三種類型的指數範圍至少是-37至37。

#include <iostream>
using namespace std;

int main()
{
        cout.setf(ios_base::fixed, ios_base::floatfield);
        float tub = 10.0 / 3.0;
        const float million = 1.0e6;
        double mint = 10.0 / 3.0;
        cout << "tub = " << tub <<endl;
        cout << "tubs = " << tub * million << endl;
        cout << "mint = " << mint << endl;
        cout << "mints = " << mint * million << endl;
        return 0;
}

結果: tub = 3.333333 tubs = 3333333.250000 mint = 3.333333 mints = 3333333.333333

通常cout會刪除結尾的零,例如,將3333333.250000顯示為 3333333.25。呼叫cousetf()將覆蓋這種行為。tub和mint都被初始化為10.0/3.0=3.333333333333….,由於cout列印6位小數,因此tub和mint都是精確的。但是當程式將每個數乘以一百萬後,tub在第7個3之後就不正確的值有了誤差。然後,double型別的變數顯示了13個3,因此它至少有13位精確。這是由於float的精度限制。

讀取包含檔案:程式中的所有檔案都存在於計算機中,或位於計算機可以使用的地方。找到使用的包含檔案,看看它們包含的內容,很快發現,原始檔和標頭檔案都是知識和資訊的很好來源。

浮點常量

與整型相比,浮點數優點:

  • 可以表示整數之間的值
  • 由於有縮放因子,可以表示的範圍大得多

浮點數缺點:

  • 浮點運算的速度通常比整數運算慢
  • 精度將降低

fltadd.cpp程式:

#include <iostream>
using namespace std;

int main()
{
        float a = 2.34e22;
        float b = a +  1.0;

        cout << "a = " << a << endl;
        cout << "a - b = " << b -a << endl;
        return 0;
}

結果: a = 2.34e+22 a - b = 0

問題在於,2.34e22是一個小數點左邊有23位的數字。加上1,就是在第23位加1。但是float型別只能表示數字中的前6或前7位,因此修改第23位對這個值不會有影響。

3.4 C++算術運算子

C++中常用的運算子:

  • +運算對運算元執行加法
  • -運算
  • *運算
  • /運算
  • %運算子求模,生成第一個數除以第二個數後的餘數。例如19%6=1。如果其中一個是負數,則結果的符號滿足如下規則:(a/b)*b+a%b = a,%的運算元必須是整數。

arith.cpp

#include <iostream>
using namespace std;

int main()
{
        float a = 10.23;
        float b = 11.22;
        cout.setf(ios_base::fixed, ios_base::floatfield);

        cout << "a = "<<  a <<endl;
        cout << "a + b = " <<  a + b << endl;
        return 0;
}

結果:a = 10.230000 a + b = 21.450001

這是由於float保證6位有效位。

3.4.1 運算子優先順序和結合性

先乘除,後加減

*、/和%的優先順序相同

float logs = 120 / 4* 5;

對於上式,運算子/和的優先順序相同,因此優先順序本身不能指出程式究竟是先計算120除以4,還是先計算4*5。兩種結果截然不同。

當兩個運算子優先順序相同時,C++將看運算元的結合性是從左到右還是從右到左。從左到右意味著如果兩個優先順序相同的運算子被同時用於一個運算元,則首先應用左側的運算子。查表得,乘除是從左到右結合得,所以上述式子應先計算120/4,得到結果30再除以5。

3.4.2 除法分支

除法運算子(/)的行為取決於運算元的型別。如果兩個運算元都是整數,則C++將執行整數除法,這意味著小數部分將被丟棄,使得最後的結果是一個整數。如果其中有一個(或兩個)運算元是浮點數,則小數部分保留,結果為浮點數。

除法運算子表示了3種不同的運算:int除法、float除法、double除法。C++根據上下文來確定運算子的含義,使用相同的符號進行多種操作叫做運算子過載。

3.4.3 求模運算子

求模運算子返回整數除法的餘數,它與整數除法先結合,尤其適用於解決要求將一個量分成不同的整數單元的問題,例如:英寸轉換為英尺和英寸。

將181鎊,轉換為英石和鎊。一英石等於14磅.

#include <iostream>
using namespace std;

int main()
{
        const int lbs_per_stn = 14;

        int lbs = 181;
        int stone = lbs / lbs_per_stn;
        int pounds = lbs % lbs_per_stn;
        cout << lbs << " pounds are " << stone << " stone, "
        <<  pounds << " pound(s)."<< endl;
        return 0;
}

結果:181 pounds are 12 stone, 13 pound(s).

3.4.4 型別轉換

C++自動執行型別轉換:

  • 將一個算術型別的值賦給另一種算術型別的變數
  • 表示式種包含不同型別
  • 將引數傳遞給函式

初始化和賦值進行的轉換

C++允許將一種型別的值賦給另一種型別的變數。值將被轉換為接受變數的型別。假設so_long的型別為long,thirty的型別為short,而程式中有:

so_long = short

則進行賦值時,程式將thirty的值(short通常為16位)擴充套件為long值(通常32位)。擴充套件後將得到一個新值,這個值儲存再so_long中,而thirty的內容不變。

將一個值賦給取值範圍更大的型別通常不會導致問題。例如,將short值賦給long變數並不會改變值,只是佔位符的位元組更多而已。

然而,建一個很大的long(如2111222333)值賦給float的變數將降低精度。因為float只有6位有效數字,因此這個值將被四捨五入為2.11122E9。因此,有些轉換是不安全的。

潛在的數值轉換問題:

  • 將較大的整數轉換為較小的整型,如將long轉換為short,原來的值可能超出目標型別的取值範圍,通常只複製右邊的位元組;
  • 將浮點數轉換為整型,小數部分丟失(不是四捨五入),原來的值可能超出目標型別的取值範圍,在這種情況下,結果可能不確定;
  • 將較大的浮點型別轉換位較小的浮點型別,如將double轉換為float,精度(有效位位數)降低,,原來的值可能超出目標型別的取值範圍,在這種情況下,結果可能不確定;

將0賦值給bool變數,將被轉換為false;而非零將被轉換為true。

以{}方式初始化時進行的轉化(C++11)

C++將使用大括號的初始化稱為列表初始化,因為這種初始化常用於給複雜的資料型別提供值列表。對型別的轉化要求更嚴格。具體,列表初始化不允許縮窄,即變數的型別可能無法表示賦給它的值。例如,不允許浮點數轉換為整型。在不同的整型之間轉換或將整型轉換為浮點型可能被允許。例如:可見long變數初始化為int型別的值,因為long總是至少與int一樣長;相反轉換也可能被允許,只要int變數能夠儲存給它的long常量。

表示式中的轉換

在計算表示式時,C++將bool、char、unsigned char、signed char和short值轉換為int。具體地說,true被轉換為1,false被轉換為0,這些轉換稱為整型提升。

整型提升還包括:如果short比int短,則unsigned short型別轉換為int;如果兩種型別的長度相同,則unsigned short型別轉換為unsigned int。這種規則保證對unsigned short提升時不會損失資料。

將不同型別進行算術運算時,也會進行一些轉換。比如將int和float相加。當運算涉及兩種型別時,較小的型別被轉換為較大的型別。編譯器通過校驗表來確定算術表示式中執行的轉換,校驗表如下:

  • 如果有一個運算元的型別為long double,則將另一個運算元轉換為long double;
  • 否則,如果有一個運算元的型別為double,則將另一個運算元轉換為double;
  • 否則,如果有一個運算元的型別為float,則將另一個運算元轉換為float;
  • 否則,說明運算元都是整型,因此執行整型提升;
  • 在這種情況下,如果兩個運算元都是有符號或無符號的,且其中一個運算元的級別比另一個低,則轉換為高級別的型別;
  • 如果一個運算元有符號,另一個運算元無符號,且無符號運算元的級別比有符號運算元的級別高,則將有符號運算元轉換為無符號的運算元型別;
  • 否則,如果有符號型別可表示無符號型別的所以可能值,則將無符號型別轉換為有符號運算元所屬的型別;
  • 否則,將兩個運算元都轉換為無符號版本。

引數傳遞的轉換

傳遞引數時的型別轉換通常由C++函式原型控制。

強制型別轉換

C++允許通過強制轉換機制顯示地進行型別轉換。強制型別轉換格式有兩種,例如:為將儲存在變數thorn中的int值轉換為long型別,如下:

(long)thorn//來自C
long(thorn)//C++新格式:像函式呼叫一樣

強制轉換不會改變thorn本身,而是建立一個新的、指定型別的值。

C++還引入了4個強制型別轉換符,對他們的使用要求更嚴格。後面介紹,在這四種運算子中,static_cast<>可用於將一種數值轉換為另一個種數值型別,例如將thorn轉換為long型別:

static_cast<long> (thorn)

推廣之,可以這樣做:

static_cast<typename> (value)

typecast.cpp

#include <iostream>
using namespace std;

int main()
{
        int auks, bats, coots;
        auks = 11.99 + 19.99; //相加賦值給int變數,被截斷為31,

        bats = int(11.99) + int(19.99);//先截斷,在相加
        coots = (int) 11.99 + (int) 19.99;
        cout << "auks = " << auks << ", bats = " << "bats"
        << ", coots = " << coots<< endl;

        char ch = 'Z';
        cout << "The code for " << ch <<  " is "
        << int(ch) << endl;
        cout << "Yes, the code is " << static_cast<int>(ch) << endl;
        return 0;
}

auks = 31, bats = bats, coots = 30 The code for Z is 90 Yes, the code is 90

3.4.5 C++11中的auto宣告

C++新增了一個工具,讓編譯器能夠根據初始值的型別推斷變數的型別。為此,它重新定義了auto的含義。auto是C語言的關鍵子,當很少使用。在初始化宣告中,如果使用關鍵字auto,而不指定變數型別,編譯器將把變數的型別設定成與初始值相同:

auto n = 100; //n是整型
auto x = 1.5 // x is double
auto y = 1.3e12L // y is long double 

然而,自動推斷型別並非為這種簡單情況而設計;事實上,如果將其用於這種簡單情形,甚至可能讓您誤入歧途。例如,假設要將x、y、z都指定為double,並編寫了如下程式碼:

auto x = 0.0; //浮點型
double y = 0; //浮點型
auto z = 0; //整型

處理複雜型別,如標準模組庫(STL)中的型別,自動型別推斷的優勢才能顯現出來。例如,對於下述C++98程式碼:

std:vector<double> scores;
std:vector<double>::iterator pv = scores.begin();

C++允許您將其重寫為:

std::vector<doble> scores;
auto pv = scores.begin()

3.5 總結

C++的基本型別分為兩組:

  • 由儲存為整型的值組成
  • 由儲存為浮點格式的值組成

整型通過儲存值時的記憶體量及有無符號來區分。整型從最小到最大依次是:

bool \ char \ signed char \ unsigned char \ short \ unsigned short \ int \ unsigned int \ long \ unsigned long \ long long \ unsigned long lon

3種浮點型別:

float \ double \ long double