C++_系列自學課程_第_11_課_類型轉換_《C++ Primer 第四版》
一、隱式類型轉換
在表達式中,有些操作符可以對多種類型的操作數進行操作, 例如 + 操作符的操作數可以同時有int型,也可以有float型, 這就引入了一個問題到底應該由什麼
決定表達式的值的類型。 例如:
3.1415926 + 5; //double類型 + 整型, 結果為什麼類型呢??
3.1415926 * 2 * 3 ; //double類型 * 整型 * 整型; 得到的表達式的值為什麼類型呢??
2 * 4UL; // 整型 * 無符號整型 , 得到的表達式的值的類型為什麼呢??
在C++中,為了解決這類問題引入了類型轉換的機制。
1、什麼時候會發生類型轉換
A: 在具有混合類型的表達式中,會發生類型轉換, 如上面的例子。
3.14 * 4; 在計算的時候會發生類型轉換,會將4轉換為 4.0 然後與3.14 相乘。
B:用作條件的表達式會被轉換為bool類型
int iVar;
.......
if(iVar)
.......
這裡當做if語句的條件表達式的 iVar 會發生類型轉換, 當iVar 非零時轉換為 true, 當 iVar 為0時轉換為false。
C: 當bool類型的變量、值不是 !、&&、|| 操作符的操作數時會發生類型轉換
bool bVar;
.......
int iVar;
int iResult;
iResult = iVar + bVar; //這裡bool量bVar會轉換為整型
如上所示, bool量bVar會發生類型轉換, 當bVar為真True時轉換為1, 當bVar為假false時會轉換0, 這樣就可以參與計算。
D:當賦值操作符的左右操作數的類型不一致時會發生類型轉換
int iVar;
iVar =3.1415926; //double類型的3.1415926會被轉換為int型賦值給iVar;
這裡講double類型的值轉換為int型賦值給int型
E:當其他類型的值初始化變量時會發生類型轉換
int iVar = 3.1415926; //賦值初始化,
或者
int iVar(3.1415926); //直接初始化
這裡進行初始化時會將3.1415926 轉換為int型,然後初始化iVar。
2、算術轉換
C++為內置的操作符和數據類型提供了一套默認的數據類型轉換機制,其中最為常見的就是算術轉換。 算術操作符 + 、- 、* 、/ 兩個操作數的類型不一致時
就會發生數據類型轉換, 在程序執行前,會將算術操作符兩邊的操作數轉換為同一數據類型後才能進行算術操作。算術轉換的基本原則就是在轉換後要能保證計算的
精度。 例如如果算術操作符的操作數有一個為long double 那麼就會將另外一個操作數轉換為long double類型然後進行計算。
在C語言中,會有如下基本的類型轉換特點:
注意的是float類型不會自動轉換為double類型,這一點需要特別的注意。在C++中大體保持了這樣一個轉換規則,我們可以通過sizeof操作符來看看操作結果。
Exp:
復制代碼
int main()
{
cout<<"sizeof(3.14L * 2) is:"<<sizeof(3.14L * 2)<<endl;
cout<<"sizeof(3.14 * 2 ) is:"<<sizeof(3.14 * 2)<<endl;
cout<<"sizeof(3.14f * 2) is:"<<sizeof(3.14f *2)<<endl;
cout<<endl;
cout<<"sizeof(long int) is"<<sizeof(long int)<<endl;
cout<<"sizeof('a'*'a') is:"<<sizeof('a'*'a')<<endl;
cout<<"sizeof(2L * 2) is:"<<sizeof(2L * 2)<<endl;
return 0;
}
復制代碼
程序的執行結果為:
復制代碼
[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
sizeof(3.14L * 2) is:12
sizeof(3.14 * 2 ) is:8
sizeof(3.14f * 2) is:4
sizeof(long int) is4
sizeof('a'*'a') is:4
sizeof(2L * 2) is:4
復制代碼
總結一句就是:
1、對於所有比int類型小的整型,在計算的過程中都會轉換為int型,如果其值都包含在int內,則會將操作數轉換為int型然後進行計算, 如果其值
超過了int型的表示范圍,則會轉換為 unsigned int型進行計算。 如果操作數中有long 型,則會轉換為long類型進行計算。
2、對於浮點型,如果有long double則會轉換為long double進行計算, 如果有double類型,則會轉換為double類型進行計算, 如果有float則
會轉換為float類型進行計算。
對於這個需要特別注意的就是float類型不會自動提升為double類型,這個是與整型不一樣的地方。
3、關於有符號和無符號數之間的轉換
討論的是整型的無符號數和有符號數直接的轉換, 這個是C/C++語言當中的一大難點, 前面我有一篇文章討論過這個問題。這裡再重新的描述一下。
A: 兩個操作數為 unsigned short int 和 int
如果int足夠保存unsigned short int 的值,那麼就會轉換int進行計算,如果不能保存,那麼就會轉換為unsigned int 進行計算。
B: 兩個操作數為 unsigned int 和 long int
如果long int足夠保存所有的unsigned int類型的值,那麼就會轉換為long int進行計算,否則就轉換為unsigned long int 進行計算。
C: 兩個操作數為int 和unsigned int
這個地方有點需要注意,int會轉換為unsigned int 後進行計算, 計算的結果為unsigned int類型,然後如果賦值給int 類型的話,那麼可能出現溢出
的情況, 通過截斷高位部分將unsigned int 類型的值賦值給int類型。
這個地方一般也不太需要特別的注意,但是在一些特殊的地方則需要引起特別的關注。
4、關於指針和數組
在我的以前的一篇隨筆中曾經描述過這個問題,那就是在一般的情況下,數組(名)都可以轉換為指針。例如:
int iArray[3];
int *pInt = iArray; //數組(名)轉換為指針類型,然後初始化指針變量pInt
這種轉換基於下面的理念: 數組名一個數組的首地址,並且是個常量,不可改變,不可對數組名進行賦值。
要點:
1、sizeof(iArray) ; 這個地方,不會將數組名轉換為指針。
2、定義引用的時候,不會將數組名轉換為指針
Exp:
復制代碼
int main()
{
int iArray[5] = {1,2,3,4,5};
int (& refArray)[5] = iArray;
cout<<"iArray[0]="<<refArray[0]<<endl;
return 0;
}
復制代碼
程序的執行結果如下:
[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
iArray[0]=1
5、關於bool類型的轉換規則
其他非bool類型的內置類型如果要轉換為bool值,則非零轉換為真True, 0轉換為假false。
bool類型會轉換int類型, true轉為1, false轉為0
6、枚舉類型轉換規則
枚舉類型是一種自定義的數據類型,通過關鍵字 enum來定義。枚舉類型通常用來定義一些屬性或者狀態,比方說定義一些值表示星期, 定義一些值表示文件的
打開、關閉狀態, 定義一些值表示忙、閒等等。
A: 定義枚舉類型
語法: enum 枚舉類型名 {枚舉類型可以取的值};
Exp:
enum open_mode {input, output , append } // 定義了枚舉類型 Week, 這種類型的變量可以取的值為: input、output、append
enum open_mode status = input; //定義枚舉類型變量 status,並初始化為input
要注意的是定義枚舉類型的變量時,關鍵字 enum不能省略,除非用typedef為枚舉類型定義了別名。
B:枚舉類型值
默認定義枚舉類型的時候,其第一個值默認取值為0, 後面的值其值會默認增加1,
例如:
enum file_status {close, open}; // 這裡默認close的值為0 , open的值為1
還可以指定這些值,
例如:
enum file_status {close = 10 , open}; //這裡 close = 10 , 而open的值為11
要點:
指定枚舉的值時可以有兩個值相等。
enum Points {point2d = 2, point2w,
point3d= 3, point3w}; //這一點需要引起注意, 但是一般不會這麼定義枚舉數據類型
C:枚舉類型的轉換規則
枚舉類型可以參與計算, 可以將其值轉換為int型的值, 其轉換後的值,根據枚舉類型的值確定。
typedef enum open_mode {input, output,append} OPENMODE;
OPENMODE eVar = input;
int iVar = 10 + eVar ; // 則eVar 轉換為int型的值0 ,然後計算表達式的值
二、顯示類型轉換
顯示類型轉換就是在程序中明確的指出類型轉換規則, 這樣可以覆蓋系統默認的隱式轉換規則。隱式轉換有: dynamic_cast、static_cast、const_cast、
reinterpret_cast.
1、dynamic_cast
支持運行時識別指針或引用執行的對象,待後續專門隨筆進行介紹。
2、const_cast
const_cast 就是將const對象的const屬性強制的取消,使我們可以顯示的修改對象的值。這裡沒有搞清楚, 不知道哪位大神,對這裡理解比較透徹,請
不吝指教。
3、static_cast
static_cast和C語言中強制類型轉換一樣, 只是表示方法不一樣而已。 當然C++也支持C語言風格的強制類型轉換。
char chVar = static_cast<char>(1234); //顯示的將1234轉換為char類型。
這裡要注意的static_cast後面用<> 將要轉換為的數據類型表示出來,而用( )操作符將要轉換的操作數括起來。
C語言風格的強制類型轉換:
char chVar = (char)1234;
這裡不進行過多的討論。
4、reinterpret_cast
reinterpret_cast通常為操作數的位模式提供重新解釋的機制。
例如:
復制代碼
int iVar;
int *pInt = &iVar;
*pInt = 12345;
char *pCh = reinterpret_cast<char *>(pInt);
復制代碼
這裡我們如果我們通過pCh訪問內存的話, 那麼系統就會將變量 iVar占用的內存 4個字節(32bit的系統)解釋為char型數組;值得注意的是iVar 和 *pInt 訪問
這一塊內存區域的時候,還是會將這一塊區域解釋為int型, 就是說強制的 reinterpret_cast 並不能改變原指針指向的數據類型或者原變量的數據類型。這裡特別需要
注意。