原文地址不詳
字符型變量的值實質上是一個8位的整數值,因此取值范圍一般是-128~127,char型變量也可以加修飾符unsigned,則unsigned char 型變量的取值范圍是0~255(有些機器把char型當做unsighed char型對待, 取值范圍總是0~255)。
● 如果一個運算符兩邊的運算數類型不同,先要將其轉換為相同的類型,即較低類型轉換為較高類型,然后再參加運算,轉換規則如下圖所示。
double ←── float 高
↑
long
↑
unsigned
↑
int ←── char,short 低
● 圖中橫向箭頭表示必須的轉換,如兩個float型數參加運算,雖然它們類型相同,但仍要先轉成double型再進行運算,結果亦為double型。 縱向箭頭表示當運算符兩邊的運算數為不同類型時的轉換,如一個long 型數據與一個int型數據一起運算,需要先將int型數據轉換為long型, 然后兩者再進行運算,結果為long型。所有這些轉換都是由系統自動進行的, 使用時你只需從中了解結果的類型即可。這些轉換可以說是自動的,但然,C語言也提供了以顯式的形式強制轉換類型的機制。
● 當較低類型的數據轉換為較高類型時,一般只是形式上有所改變, 而不影響數據的實質內容, 而較高類型的數據轉換為較低類型時則可能有些數據丟失。
賦值中的類型轉換
當賦值運算符兩邊的運算對象類型不同時,將要發生類型轉換, 轉換的規則是:把賦值運算符右側表達式的類型轉換為左側變量的類型。具體的轉換如下:
(1) 浮點型與整型
● 將浮點數(單雙精度)轉換為整數時,將舍棄浮點數的小數部分, 只保留整數部分。
將整型值賦給浮點型變量,數值不變,只將形式改為浮點形式, 即小數點后帶若干個0。注意:賦值時的類型轉換實際上是強制的。
(2) 單、雙精度浮點型
● 由於C語言中的浮點值總是用雙精度表示的,所以float 型數據只是在尾部加0延長為doub1e型數據參加運算,然后直接賦值。doub1e型數據轉換為float型時,通過截尾數來實現,截斷前要進行四舍五入操作。
(3) char型與int型
● int型數值賦給char型變量時,只保留其最低8位,高位部分舍棄。
● char型數值賦給int型變量時, 一些編譯程序不管其值大小都作正數處理,而另一些編譯程序在轉換時,若char型數據值大於127,就作為負數處理。對於使用者來講,如果原來char型數據取正值,轉換后仍為正值;如果原來char型值可正可負,則轉換后也仍然保持原值, 只是數據的內部表示形式有所不同。
(4) int型與1ong型
● long型數據賦給int型變量時,將低16位值送給int型變量,而將高16 位截斷舍棄。(這里假定int型占兩個字節)。
將int型數據送給long型變量時,其外部值保持不變,而內部形式有所改變。
(5) 無符號整數
● 將一個unsigned型數據賦給一個占據同樣長度存儲單元的整型變量時(如:unsigned→int、unsigned long→long,unsigned short→short) ,原值照賦,內部的存儲方式不變,但外部值卻可能改變。
● 將一個非unsigned整型數據賦給長度相同的unsigned型變量時, 內部存儲形式不變,但外部表示時總是無符號的。
/*例:賦值運算符舉例 */
main()
{ unsigned a,b;
int i,j;
a=65535;
i=-1;
j=a;
b=i;
printf("(unsigned)%u→(int)%d\n",a,j);
printf("(int)%d→(unsigned)%u\n",i,b);
}
運行結果為:
(unsigned)65535→(int)-1
(int)-1→(unsigned)65535
● 計算機中數據用補碼表示,int型量最高位是符號位,為1時表示負值,為0時表示正值。如果一個無符號數的值小於32768則最高位為0,賦給 int型變量后、得到正值。如果無符號數大於等於32768,則最高位為1, 賦給整型變量后就得到一個負整數值。反之,當一個負整數賦給unsigned 型變量時,得到的無符號值是一個大於32768的值。
● C語言這種賦值時的類型轉換形式可能會使人感到不精密和不嚴格,因為不管表達式的值怎樣,系統都自動將其轉為賦值運算符左部變量的類型。
● 而轉變后數據可能有所不同,在不加注意時就可能帶來錯誤。 這確實是個缺點,也遭到許多人們批評。但不應忘記的是:c面言最初是為了替代匯編語言而設計的,所以類型變換比較隨意。當然, 用強制類型轉換是一個好習慣,這樣,至少從程序上可以看出想干什么。
C語言規定,不同類型的數據需要轉換成同一類型后才可進行計算,在整型、實型和字符型數據之間通過類型轉換便可以進行混合運算(但不是所有類型之間都可以進行轉換)
.當混合不同類型的變量進行計算時,便可能會發生類型轉換
相同類型的數據在轉換時有規則可循:
字符必須先轉換為整數(C語言規定字符類型數據和整型數據之間可以通用)
short型轉換為int型(同屬於整型)
float型數據在運算時一律轉換為雙精度(double)型,以提高運算精度(同屬於實型)
賦值時,一律是右部值轉換為左部類型
[注]
當整型數據和雙精度數據進行運算時,C先將整型數據轉換成雙精度型數據,再進行運算,結果為雙精度類型數據
當字符型數據和實型數據進行運算時,C先將字符型數據轉換成實型數據,然后進行計算,結果為實型數據
2.4 數據類型轉換在C語言的表達式中,准許對不同類型的數值型數據進行某一操作或混合運算。當不同類型的數據進行操作時,應當首先將其轉換成相同的數據類型,然后進行操作。數據類型轉換有兩種形式,即隱式類型轉換和顯示類型轉換。
2.4.1 隱式類型轉換所謂隱式類型轉換就是在編譯時由編譯程序按照一定規則自動完成,而不需人為干預。因此,在表達式中如果有不同類型的數據參與同一運算時,編譯器就在編譯時自動按照規定的規則將其轉換為相同的數據類型。
C語言規定的轉換規則是由低級向高級轉換。例如,如果一個操作符帶有兩個類型不同的操作數時,那么在操作之前行先將較低的類型轉換為較高的類型,然后進行運算,運算結果是較高的類型。更確切地說,對於每一個算術運算符,則遵循圖2-2所示的規則。
圖2-2 數據類型轉換規則之一
圖2-2 數據類型轉換規則之一
注意:在表達式中,所有的float類型都轉換為double型以提高運算精度。
在賦值語句中,如果賦值號左右兩端的類型不同,則將賦值號右邊的值轉換為賦值號左邊的類型,其結果類型還是左邊類型。
因為函數參數是表達式,因此,當參數傳遞給函數時,也發生類型轉換。具體地說,char和short均轉換為int;float轉換為double。這就是為什么我們把函數參數說明為int和double,盡管調用函數時用char和float .
也可以將圖2-2所示的規則用圖2-3表示。圖2-3中的水平箭頭表示必定轉換,縱向箭頭表示兩個操作對象類型不同時的轉換方向。
圖2-3 數據類型轉換規則之二
下面舉行說明類型轉換的規則。例如執行:
x=100+'a'+1.5 * u+f/'b'-s * 3.1415926
其中,u為unsigned型,f為float型,s為short型,x為float型。式中右面表達式按如下步驟處理:
(1)首先將'a'、'b'和s換成int,將1.5和f轉換為double型。
(2)計算100+'a',因'a'已轉換為int型,於是此運算結果為197。
(3)計算1.5*u,由於1.5已轉換為double,u是unsigned型,於是首先u轉換為double,然后進行運算,運算結果為double。
(4)計算197+1.5 * u,先將197轉換為double(如197.00…00),其結果為double。
(5)計算f/ 'b',f已轉換為double,'b'已轉換為int,於是先將'b'再轉換為double,其結果為double。
(6)計算(197+1.5 * u)+f / 'b',者均為double,於是結果也為double。
(7)計算s * 3.1415926,先將s由int轉換為double,然后進行運算,其結果為double。
(8)最后與前面得的結果相減,結果為double。
(9)最后將表達式的結果轉換為float並賦給x。
2.4.2 顯式類型轉換顯示類型轉換又叫強制類型轉換,它不是按照前面所述的轉換規則進行轉換,而是直接將某數據轉換成指定的類型。這可在很多情況下簡化轉換。例如,
int i;
…
i=i+9.801
按照隱式處理方式,在處理i=i+9.801時,首先i轉換為double型,然后進行相加,結果為double型,再將double型轉換為整型賦給i。
int i;
…
i=i+(int)9.801
這時直接將9.801轉換成整型,然后與i相加,再把結果賦給i。這樣可把二次轉換簡化為一次轉換。
顯示類型轉換的方法是在被轉換對象(或表達式)前加類型標識符,其格式是:
(類型標識符)表達式
例如,有如下程序段:
main()
{
int a,b;
float c;
b=a+int(c);
printf("b=d% \ n",b);
}
在上述程序的運行過程中,在執行語句b=a+int(c)時,將c的值臨時強制性轉化為int型,但變量c在系統中仍為實型變量,這一點很重要,不少初學者在這個問題上忽略了這個問題。
2.5 運算符和表達式
2.5.1 運算符和表達式概述
1.表達式一個表達式包含一個或多個操作,操作的對象稱作運算元(或叫作操作數),而操作本身通過運算符體現的。例如a、a-b、c=9.801等都是一個表達式。
一個表達式完成一個或多個操作,最終得到一個結果,而結果的數據類型由參加運算的操作決定。最簡單的表達式是只含一個常量或變量的表達式,即只含一個操作數而不含運算符。
C語言中表達式的種類十分豐富,主要有如下一些:
n 算術表達式:進行一般的計算。
n 賦值表達式:進行賦值操作。
n 關系表達式:進行比較判斷。
n 邏輯表達式:進行邏輯比較判斷。
n 條件表達式:進行條件滿足與否的判斷。
n 逗號表達式:實際上是一種復雜運算,可以包含多個算術表達式。
2.C語言的操作符C語言的特點之一是具有豐富和使用靈活的運算符,概括起來它有如下的幾類運算符:
n 算術運算符。
n 賦值運算符(包括符合賦值運算符)。
n 關系運算符。
n 邏輯運算符。
n 條件運算符。
n 逗號運算符。
n 位運算符。
n 指針運算符。
n 求字節運算符(可以歸並到函數的應用中去,它是通過函數sizeof()來進行運算的)。
n 強制類型轉換運算符。
這些運算符如表2-4所示。
表2-4 C語言中的運算符
名稱操作符自增,自減
+ +,- -
邏輯與、或、非
&&,︱︱,!
續表2-4
名稱操作符指針操作及引用
*,&
加、減、乘、除、求模運算
+,-,*,/,%
關系操作符
<,<=,>,>=,==,!=
按位與、或、異或、求反
&,丨,^,~
逗號表達式
,
類型轉換
()
移位運算
< <,> >
條件運算
?:
求占用的字節數
sizeof
賦值
=,+ =,- =,*=,/ =,% =
先看程序:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char**argv){
unsigned int right = 1;
char left = -1;
if(left < right)printf("%d < %d\n", left, right);
else if(left == right)printf("%d = %d\n", left, right);
else printf("%d > %d\n", left, right);
system("PAUSE");
return 0;
}
運行結果是:-1 > 1
解釋:按步驟進行.
1. 如果其中一個操作數為long double類型,則另一個操作數被轉換為long double.
2. 否則,如果其中一個操作數為double, 則另一個操作數被轉換為double.
3. 否則,如果其中一個操作數為float, 則另一個操作數也轉換為float.
4. 否則,兩個操作數進行 "整型升級":
a. 如果其中一個操作數為unsigned long int, 則另一個操作數也被視為unsigned long int.
b. 否則,如果其中一個操作數為long int,而另一個操作數類型是unsigned int, 並且long int能夠表示unsigned int的所有值,則另一個操作數也被視為long int;如果long int不能表示unsigned int的所有值,則兩個數都被視為unsigned long int.
c. 否則, 如果其中一個操作數是long int,則另一個操作數也被視為long int.
d. 否則, 如果其中一個操作數是unsigned int, 則另一個操作數也被視為unsigned int.
e. 否則, 兩個操作數都被視為int.
例題1:
char a=0xb6; short b=0xb600; int c=0xb6000000; if(a==0xb6) puts("a"); //左邊為有符號數,右邊無符號數 if(b==0xb600) puts("b"); if(c==0xb6000000) puts("c"); //結果:gcc只打印出c(char默認為signed char)
==為一種運算符,則兩邊的值會進行類型轉換為int型。見第一個圖
gcc將三條語句解釋為
if(0xffffffb6==0x000000b6) puts("a"); if(0xffffb600==0x0000b600) puts("b"); if(0xb6000000==0xb6000000) puts("c");
例題2:
同樣是有關“整形提升”
無符號數擴位補0
有符號書擴位補符號位,符號位為0則補0為1則補1
#include <stdio.h>
int main()
{
char ca;
unsigned char ucb;
unsigned short usc;
ca = 128; //0x80
ucb =128; //0x80
usc = ca + ucb; //0x0000=0xffffff80 + 0x00000080 然后截斷取低四位(后同)
printf("%d\n", usc);
usc = ca + (short)ucb; //0x0000=0xffffff80 + 0x0000(0080) unsigned char -> short ->int
printf("%d\n", usc);
usc = (unsigned char)ca + ucb; //0x0100 = 0x000000(80) + 0x00000080
printf("%d\n", usc);
usc = ca + (char)ucb; //0xff00 = 0xffffff80 + 0xffffff(80)
printf("%d\n", usc);
getchar();
return EXIT_SUCCESS;
}
結果:0 0 256 65280
同時可以看到char<->unsigned char, short<->unsigned short 轉換中,與類型等長的部分其實是相同的,不同的是截斷去掉的那部分
char -128 127
unsigned char 0 256
0-127之間二者沒有差別
例如:
unsigned char uca=128;
printf("%x\n",(char)uca); //打印出來: ffffff80,unsigned char -> char若越界的話會整形提升。