在執行算術運算時,計算機比C語言的限制更多。爲了讓計算機執行算術運算,一般要求操做數有相同的大小(即位的數量相同),而且要求存儲的方式也相同。計算機可能能夠直接將兩個16位整數相加,可是不能直接將16位整數和32位整數相加,也不能直接將32位整數和32位浮點數相加。另外一方面,C語言容許在表達式中混合使用基本數據類型。在單獨一個表達式中能夠組合整數、浮點數,甚至是字符。固然,在這種狀況下C語言編譯器可能須要生成一些指令將某些操做數轉換成不一樣類型,使得硬件能夠對錶達式進行計算。例如,若是對16位int型數和32位long int型數進行加法操做,那麼編譯器將安排把16位int型值轉換成32位值。若是是int型數據和float型數據進行加法操做,那麼編譯器將安排把int型值轉換成爲float格式。這個轉換過程稍微複雜一些,由於int型值和float型值的存儲方式不一樣。由於編譯器能夠自動處理這些轉換而無需程序員介入,因此這類轉換稱爲隱式轉換(implicit conversion)。C語言還容許程序員經過使用強制運算符執行顯式轉換(explicit conversion)。首先討論隱式轉換,,執行隱式轉換的規則有些複雜,主要是由於C語言有大量不一樣的基本數據類型(6種整型和3種浮點型,這還不包括字符型)。 html
當發生下列狀況時會進行隱式轉換: 程序員
.當算術表達式或邏輯表達式中操做數的類型不相同時。(C語言執行所謂的經常使用算術轉換。) 面試
.當賦值運算符右側表達式的類型和左側變量的類型不匹配時。 編程
.當函數調用中使用的參數類型與其對應的參數的類型不匹配時。 ubuntu
.當return語句中表達式的類型和函數返回值的類型不匹配時。 c#
隱式類型轉換規則:
C語言自動轉換不一樣類型的行爲稱之爲隱式類型轉換 ,轉換的基本原則是:低精度類型向高精度類型轉換,具體是:
int -> unsigned int -> long -> unsigned long -> long long -> unsigned long long -> float -> double -> long double
注意,上面的順序並不必定適用於你的機器,好比當int和long具備相同字長時,unsigned int的精度就會比long的精度高(事實上大多數針對32機的編譯器都是如此)。另外須要注意的一點是並無將char和short型寫入上式,緣由是他們能夠被提高到int也可能被提高到unsigned int。
提高數據的精度一般是一個平滑無損害的過程,可是下降數據的精度可能致使真正的問題。緣由很簡單:一個較低精度的類型可能不夠大,不能存放一個具備更高精度的完整的數據。一個1字節的char變量能夠存放整數101但不能存放整數12345。當把浮點類型數據轉換爲整數類型時,他們被趨零截尾或舍入。
函數
當把有符號操做數和無符號操做數整合時,會經過把符號位當作數的位的方法把有符號操做數"轉換"成無符號的值.這條規則可能會致使某些隱蔽的編程錯誤。 ui
假設int型的變量i的值爲-10,並且unsigned int型的變量u的值爲10。若是用<運算符比較變量i和變u,那麼指望的結果應該是1(真)。可是,在比較前,變量i轉換成爲unsigned int類型。由於負數不能被表示成無符號整數,因此轉換後的數值將再也不爲-10,而是一個大的正數(將變量i中的位看做是無符號數).所以i<u比較的結果將爲0。spa
因爲此類陷阱的存在,因此最好盡最避免使用無符號整數,特別是不要把它和有符號整數混合使用。 .net
先來看一段簡單的代碼:
1 #include <stdio.h>
2
3 int array[] = {1, 2, 3, 4, 5, 6, 7};
4 #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
5
6 int main()
7 {
8 int i = -1;
9 int x;
10
11 if(i <= TOTAL_ELEMENTS - 2) {
12 x = array[i + 1];
13 printf("x = %d.\n", x);
14 }
15
16 printf("now i = %d.\n", TOTAL_ELEMENTS);
17
18 return 0;
19 }
執行結果:
randy@ubuntu:~/C_Language$ ./a.out
now i = 7.
是否是很奇怪?爲何沒有打出line13的x = ?。
是這樣的。這個小例子有三點值得注意:
1.sizeof()是運算符,返回類型是無符號的,即非負數。
2.if語句在singned int和unsigned int之間進行判斷語句,根據C語言的整型提高規則,int -> unsigned int 。
3.i = -1被升級爲無符號型,值到底是多少?這要用到整型轉換規則:K&R上這樣解釋,將任何整數轉換爲某種指定的無符號數類型數的方法是:以該無符號數類型可以表示的最大值加1爲摸,找出與此整數同餘的最小的非負值。聽着很拗口,其實說白了,只要知道原整數的二進制表達方法,再用要即將轉換的類型去解析,就獲得升級後的值了。 好比-1,負數在計算機裏用補碼錶示爲0xffffffff,那升級成無符號型以後,值就是0xffffffff,顯然比TOTAL_ELEMENTS(7)大。
---------------------------------------------------------------------------------------------
強制類型轉換:
一般咱們應該避免自動類型轉換,當咱們須要手動指定一個準確的數據類型時,咱們能夠用強制類型轉換機制來達到咱們的目的,使用方法很簡單,在須要強制轉換類型的變量或常量前面加上(type),例如(double)i; 即把變量 i 強制轉換成double型。
思考下面這個例子:
long int i;
int j = 10000;
i = j*j; /*wrong*/
乍看之下,這條語句沒有問題。表達式j*j的值是1000000,而且變量i是long int型的,所
以i應該能很容易地存儲這種大小的值,不是嗎?問題是,當兩個int型值相乘時,結果也應該
是int類型的,可是j*j的結果太大,以至於在某些機器上沒法表示成int類型.在這樣的機器
上,會給變量i賦一個無心義的值。幸運的是,可使用強制類型轉換避免這種問題的發生:
i=(long int)j*j
由於強制運算符的優先級高於*,因此第一個變量j會被轉換成long int類型,同時也迫使第
二個j進行轉換。
注意語句
i==(long int)(j*j)/**WRONG***/
是不對的,由於溢出在強制類型轉換以前就己經發生了。
Q&A:
問:若是"溢出"會發生什麼?好比,兩個數相加的結果過大而沒法存儲.
答:這取決於數是有符號型的仍是無符號型的。當溢出發生在有符號數的操做上時,依據C語言的標準,
結果是"未定義的"。咱們沒法準確說出結果是什麼,由於這依賴於機器的行爲。程序甚至可能會
異常中斷(對除以零的典型反應)。
可是,當溢出發生在無符號數的操做上時,結果是定義了的:能夠得到正確答案對2n進行取模運算
的結果,這裏的n是用於存儲結果使用的位數。例如,若是用1加上無符號的16位數65535,那麼結
果確定是65536 (已經溢出,但計算機能正確表示其值).
int t1() { short int i = 65535; short int j = 1; unsigned short int uj = 1; unsigned short int ui = 65535; printf("i + j = %d\n" , i + j); printf("i + uj = %d\n" , i + uj); printf("ui + uj = %d\n" , ui + uj); printf("ui + 1 = %d\n" , ui + 1); } /* root@oucaijun:/work/dcc# gcc 1.c ;./a.out i + j = 0 i + uj = 0 ui + uj = 65536 ui + 1 = 65536 */
---------------------------------------------------------------------------------------------
《C語言程序設計:現代方法》第7章 基本類型。