2.4 運算符及表達式
在C語言中,運算符和表達式的數量之多,在高級語言中是少見的。正是豐富的運算符和表達式使C語言的功能十分完善,這也是C語言的主要特點之一。
C語言運算符的類別見表2.5。
表2.5 C語言運算符的類別
● 優先級:在一個表達式中如果有多個運算符時,計算是有先后次序的,這種計算的先后次序稱為相應運算符的優先級。
● 結合性:當一個運算對象兩側運算符的優先級別相同時,進行運算(處理)的結合方向稱為運算符的結合性。按從右向左的順序運算,稱為右結合性;按從左向右的順序運算,稱為左結合性。
在表達式中,各運算對象參與運算的先后順序不僅要遵守運算符優先級別的規定,還要受運算符結合性的制約,以便確定是自左向右進行運算,還是自右向左進行運算。 運算符的結合性也是C語言的特點之一。
本節僅介紹算術運算符、賦值運算符、自增、自減運算符、逗號運算符和求字節數運算符,以及相應的表達式。
2.4.1 算術運算
1.基本算術運算符
基本算術運算符包括1個單目運算符和5個雙目運算符,其名稱、示例及運算功能見表2.6。
表2.6 算術運算符
① 關于除法運算符/。不能用÷表示除。另外應注意,兩個整數相除結果為整數,例如, 7/2的結果值為3,舍去小數部分。如果參加運算的兩個數中有一個數為實數,則結果是double型的,因為所有實數都按double型進行運算。
② 關于取余運算符%。運算符兩側的數據必須為整型數據。結果按下式計算:余數=被除數 - 除數*商。例如,7%3的結果為1。
2.算術表達式
用算術運算符、圓括號將運算對象連接起來的符合C語法規則的表達式稱為算術表達式。例如,下面是一個合法的C語言算術表達式:
(a-b)/c*2+'a'+-15%4
C 語言算術表達式的書寫形式與數學表達式的書寫形式是有區別的,在使用時要注意以下4點。
① C語言表達式中的乘號不能省略。在數學中,5a、5×a、5·a都是合法的,但在C語言中只能寫成5*a,初學者應謹慎。
② C語言表達式中只能使用合法用戶標識符。例如,數學表達式πr2相應的C語言表達式應寫成:3.1415926*r*r。
③ C語言表達式中的所有內容必須書寫在同一行上,不允許有分子分母、上下標等形式,必要時要利用圓括號保證運算的順序。例如,數學表達式④ C語言表達式中不允許使用方括號和花括號,只能使用圓括號幫助限定運算順序。可以使用多層圓括號,但左右括號必須配對,運算時從內層圓括號開始,由內向外依次計算表達式的值。
3.算術運算符的優先級與結合性
表2.7所示為括號運算符和算術運算符的優先級和結合性。
表2.7 括號運算符和算術運算符的優先級和結合性
4.基本類型數據間的混合運算
如果一個表達式中運算對象的數據類型不同,則應當首先將其轉換為同一種類型,然后再進行運算。轉換類型的方法有兩種:一種是自動類型轉換,另一種是強制類型轉換。
(1)自動類型轉換
前已述及,字符型數據可以與整型數據通用,因此,整型、實型(包括單、雙精度數據)、字符型數據間可以混合運算。例如,以下表達式:
0.5+2*'a'+15-1.23456789
是合法的。在運算過程中,C 語言遇到兩種不同數據類型的數值進行運算時,會將某個數值做適當的類型轉換,然后再進行運算。類型轉換的規則如圖2.4所示。圖中,橫向箭頭為必定的轉換,即單精度數或字符型數或短整型數,都必須無條件地轉換成橫向箭頭左側的數據類型(char型轉換為int型,short型轉為int型,float型轉換成double型),然后再進行運算。而圖中縱向箭頭(箭頭方向表示數據類型級別的高低,由低向高轉換)是當一個運算符兩側的數據類型不相同時,先將級別低的數據類型轉換成它們之間級別高的數據類型,然后進行運算。例如,int型與long型數據進行運算,先將int型的數據轉換成long型,然后兩個同類型(long型)數據進行運算,結果為long型。假設已指定m為int型變量,x為float型變量,y為double型變量,有下面的表達式:
5+'b'-x/2+y*m
其運算過程為:
① 進行5+'b'的運算。先將'b'轉換成整數98,運算結果為整數103。
② 進行x/2的運算。先將x與2都轉換成double型,運算結果為double型。
③ 將①與②的結果相減。先將整數 103 轉換成 double 型(小數點后加若干個 0,即103.000…00),然后再相減,結果為double型。
④ 進行y*m的運算。先將m轉換成double型,運算結果為double型。
⑤ 將④與③的結果相加,結果為double型。
(2)強制類型轉換
強制類型轉換是通過類型轉換運算符來實現的。其一般形式為:
(類型標識符) (表達式)
其功能是把表達式的運算結果強制轉換成類型標識符所表示的類型。
例如:
(double)a /* 將a轉換成double型 */
(int)(x+y) /* 將x+y的和轉換成int型 */
(float)(10*5) /* 將10*5的積轉換成float型 */
在使用強制轉換時應注意以下問題。
① 類型標識符和表達式都必須加括號(單個變量可以不加括號),如果把(int)(x+y)寫成(int)x+y,則成了把x轉換成int型之后再與y相加。
② 無論強制轉換或是自動轉換,都只是為了本次運算的需要而對數據長度進行的臨時性轉換,并不改變數據定義時的類型。
③ 強制類型轉換的優先級高于自動類型轉換。
【例2.6】 強制類型轉換示例。
程序代碼如下:
#include "stdio.h"
main( )
{
int i=1,j=5,k1,k2,k3;
float x=5.7,y=2.8,z1,z2,z3;
k1=(int)x; /* 將x強制轉換成整數5(小數部分被截去)賦給k1 */
k2=(int)(x)/y; /* 將x強制轉換成整數5,再除以y,賦給k2 */
k3=(int)(x/y); /* 將x除以y的值強制轉換成整數,再賦給k3 */
z1=(float)i; /* 將i強制轉換成實數1.0賦給z1 */
z2=(float)i/j; /* 將i強制轉換成實數1.0,再除以j,賦給z2 */
z3=(float)(i/j); /* 計算i/j的值為0,再強制轉換成實數0.0賦給z3 */
printf("k1=%d\n",k1);
printf("k2=%d\n",k2);
printf("k3=%d\n",k3);
printf("z1=%f\n",z1);
printf("z2=%f\n",z2);
printf("z3=%f\n",z3);
}
程序運行結果如下:
k1=5
k2=1
k3=2
z1=1.000000
z2=0.200000
z3=0.000000
2.4.2 賦值運算
1.賦值運算符和賦值表達式
在C語言中,稱“=”為賦值運算符,它不同于算術中的等號。相等在C語言中是用“==”表示的。用賦值運算符將一個變量和一個表達式連接起來的式子稱為賦值表達式。其一般形式為:
變量=表達式
賦值表達式的功能是,將賦值運算符右邊表達式的值存放到以左邊變量名為標識的存儲單元中。
賦值表達式的值就是被賦值后賦值號左邊變量的值。例如,賦值表達式“b=5”運算后,有兩層意思:一是使變量b的值為5,即將5放到變量b對應的存儲單元中,不論變量b的原值是多少,執行上述賦值操作后(或稱賦值運算),b的值更新為5;二是求得賦值表達式“b=5”的值是5。
說明:
① 賦值運算符“=”的左邊必須是變量,右邊的表達式可以是單一的常量、變量、函數調用或表達式。例如,下面都是合法的賦值表達式:
x=10
y=x+10
y=sqrt(2)
② 賦值運算符“=”不同于數學中使用的等號,它沒有相等的含義。例如,x=x+1,其含義是取出變量x中的值加1后,再存入變量x中。
③ 賦值運算符的結合性為“右結合性”。例如:
x=y=z=8 等價于 x=(y=(z=8))
運算時,先求賦值表達式“z=8”的值(得8),其值再賦給變量y(得8),再把表達式“y=8”的值賦給x(得8)。
下面是一些賦值表達式的例子:
x=8+(y=5) /* 賦值表達式的值為13,x的值為13,y的值為5 */
x=(y=2)+(z=6) /* 賦值表達式的值為8,x的值為8,y的值為2,z的值為6 */
x=(y=6)/(z=2) /* 賦值表達式的值為3,x的值為3,y的值為6,z的值為2 */
將賦值表達式作為表達式的一種,使賦值操作不僅可以出現在賦值語句中,而且可以以表達式形式出現在其他語句(如循環語句)中,這是C語言靈活性的一種表現。
2.賦值運算中的類型轉換
當賦值運算符“=”左邊變量的類型和右邊表達式值的類型不一致時,首先要把賦值運算符“=”右邊表達式值的類型強制轉換為左邊變量的類型,然后再進行賦值。轉換規則見表2.8。
表2.8 賦值運算中數據類型的轉換規則
#include "stdio.h"
main( )
{
unsigned short i;
short j=-6;
i=j;
printf("%u",i);
}
程序運行結果為:
65530
賦值情況如圖2.5所示。
思考:如果j為6,則運行結果是什么?
3.復合賦值運算符
先看一段代碼:
int a=10;
a=a+10;
上面的代碼執行后,變量a的值是20。可以這樣理解,假設用a表示銀行存款,原先有10萬元,現在再存入10萬元,那么新的存款就等于舊存款加上10萬元,即20萬元,用C編程語言表達就是“a=a+10;”。
在C語言中這樣的自加操作可以用另一種方式表達,并且采用這種表達方式,計算機的運算速度比較快。例如,“a=a+10;”的另一種運算速度較快的表達方式為“a+=10;”。
在C語言中,“+=”被定義為一種新的自運算符(“+”與“=”要連著,中間不能有空格),它實現的操作就是將其左邊的量在自身的基礎上加上右邊表達式的值。同樣的減、乘、除、求余等雙目運算符都可以與賦值運算符結合形成自運算符-=、*=、/=、%=等。
C 語言將這種在賦值運算符之前加上雙目運算符構成的新的自運算符稱為復合賦值運算符,共有10種,如表2.9所示。
表2.9 復合賦值運算符
例如,“x*=y+8”中的“+”運算符優先級高于“*=”運算符,應先進行“+”運算,故等價于“x=x*(y+8)”,而不等價于“x=x*y+8”。
又如“k+=j+=i+8”表達式等價于“k=k+(j=j+(i+8))”,當 i=2,j=12,k=10 時,其計算過程如下:
因為表達式語句中“+”優先級高于“+=”,故先運算i+8值為10;同一級的兩個“+=”因結合性為由右向左,先運算右面的“+=”(即j+=10 的值為22),再運算左面的“+=”(即k+=22的值為32)。
C 語言采用這種復合賦值運算符,一是為了簡化程序,使程序精練,二是為了提高編譯效率,因為從編譯的角度來看,它可以生成更短小的匯編代碼。
2.4.3 自增、自減運算
當復合賦值運算是自加1或自減1時,C語言提供了更為優化的運算符—自增(++)、自減(--)運算符。
設整型變量a,初值為10。要實現對其加1,已知可以有以下兩種寫法:
● 方法1:a=a+1;
● 方法2:a+=1;
現在還有方法3,并且是最好的方法,即“++a;”或者“a++;”。
也就是說,只有在自加1的情況下,代碼++a或a++可以生成最優化的匯編代碼。
同樣,自減1操作也有對應的運算符:--a或a--。
設a原值為10,則執行--a或者a--后,a的值都為9。
所以,自增“++”和自減“--”運算符的作用是使運算對象的值增1或減1,它們是兩個單目運算符,可置于運算對象的左側或右側,例如,++i、i++、--i、i--等都屬于合法的表達式。參加自增、自減運算的運算對象只能是變量而不能是表達式或常量。表2.10列出了自增、自減運算符的種類和功能。
表2.10 自增、自減運算符
++i 1 /* 先使i的值加1,然后再使用i,表達式的值是i加1之后的值為4 */
i++ 1 /* 先使用i的值,然后再使i的值加1,表達式的值是i加1之前的值為3 */
所以賦值表達式j=++i相當于i=i+1和j=i,賦值表達式的值是4,變量i的值也是4。而
賦值表達式j=i++則相當于j=i和i=i+1,賦值表達式的值是3,變量i的值是4。
例如,已知定義語句:
int a=3;b=5,c;
則有:
(1)c=(++a)*b;
等價于:a=a+1;
c=a*b;
結果:c的值為20。
(2)c=(a++)*b;
等價于:c=a*b;
a=a+1;
結果:c的值為15。
【例2.8】 自增、自減運算符使用示例。
程序代碼如下:
#include "stdio.h"
main( )
{
int i=5,j,k;
j=++i; /* 先使i的值變為6,j的值為6 */
i=5;
k=i++; /* k的值為5,然后i變為6 */
printf("i=%d,j=%d,k=%d\n",i,j,k);
printf("j=%d,k=%d\n",++j,k++); /* 先輸出k的值5和j加1以后的值7,再使k加1 */
}
程序運行結果如下:
i=6, j=6, k=5
j=7, k=5
說明:
① 自增、自減運算符(++、--),只能用于變量,不能用于常量和表達式,例如,2++或(x+y)++都是不合法的。
② ++和--的結合方向是“右結合性”,其優先級高于基本算術運算符,與負號運算符為同一優先級。例如-i++,因為“-”運算符和“++”運算符優先級相同,而結合方向為“自右至左”,即它相當于-(i++)。
2.4.4 逗號運算
逗號運算符即逗號“,”。在 C 語言中可以用逗號將若干表達式連接起來,構成一個逗號表達式。其一般形式為:
表達式1,表達式2,表達式3,……,表達式n
逗號表達式的運算過程是,依次求解表達式1的值,表達式2的值,……,最后求解表達式n的值。整個逗號表達式的值和類型是表達式n的值和類型。
逗號表達式的計算順序是自左向右,因此,逗號運算符又稱為“順序求值運算符”。例如:
a=36/9,4*a
這是一個逗號表達式,由表達式a=36/9和4*a構成。求值過程先執行a=36/9=4,再執行4*a=16,而整個逗號表達式的值為16。
在有逗號運算符參與的表達式中,逗號運算符是所有運算符中優先級級別最低的。因此,下面兩個表達式的作用是不同的:
a=(2+b,a*2,a*=5)
/*是賦值表達式,將一個逗號表達式的值賦給a,a的值等于表達式a*=5的值。*/
a=2+b,a*2,a*=5
/*是逗號表達式,它包括兩個賦值表達式和一個算術表達式。*/
其實,逗號表達式無非是把若干個表達式串聯起來。在許多情況下,使用逗號表達式的目的只是想分別得到各個表達式的值,而并非一定需要得到和使用整個逗號表達式的值,逗號表達式通常用于循環語句(for語句)中,詳見第5章。
請注意,并不是任何地方出現的逗號都可以作為逗號運算符。例如,在變量說明中的逗號只起間隔符的作用,不構成逗號表達式。
2.4.5 sizeof運算符
sizeof運算符是一個單目運算符,它返回變量或數據類型的字節長度。其一般形式為:
sizeof(類型標識符) 或 sizeof(變量名)
例如:
● sizeof(double)表達式的值為8;
● sizeof(int)表達式的值為4。
設有下列程序段:
float f;
int i;
i=sizeof(f);
則變量i的值為4。