一、操作符
包括:算術(shù)操作符,移位操作符,位操作符,賦值操作符,單目操作符,關(guān)系操作符,邏輯操作符,條件操作符,下標(biāo)引用,函數(shù)調(diào)用,結(jié)構(gòu)體成員調(diào)用。
1、算術(shù)操作符
加、減、乘、除、取模(%)
取模的兩個(gè)操作數(shù)只能是整型值。
2、移位操作符
左移位:<< 最左邊幾位丟棄,最右邊幾位補(bǔ)0;
右移位:>> (1)邏輯移位:最右邊幾位丟棄,左邊補(bǔ)0;
(2)算術(shù)移位:最右邊幾位丟棄,左邊符號(hào)位為0,則全移入0,為1則全移入1。
標(biāo)準(zhǔn)規(guī)定無符號(hào)數(shù)都執(zhí)行邏輯移位,而有符號(hào)數(shù)執(zhí)行的是邏輯移位還是算術(shù)移位由編譯器決定,若移位的位數(shù)超過操作數(shù)的位數(shù),則具體情況由編譯器決定。
所以出現(xiàn)有符號(hào)數(shù)移位操作的程序是不可移植的。
3、位操作符
& | ^ (與,或,異或)
通常用于位操作,修改變量指定位的值。
舉例:
value的第bit_number位置1:
value |= 1 << bit_number;
value的第bit_number位置0:
value &= ~ (1 << bit_number);
測(cè)試指定位是否為1,為1則表達(dá)式非0:
value &= 1 << bit_number;
4、賦值操作符
賦值是表達(dá)式的一種,而不是某種類型的語句,所以只要允許出現(xiàn)表達(dá)式的地方都可以進(jìn)行賦值。
舉例:
a = x = y + 3;
a和x被賦的值并不是同一個(gè)值
若x的長(zhǎng)度不夠,則x被賦予的y+3值會(huì)被截短,再存儲(chǔ)于x中,之后這個(gè)被截短的值再被賦予到a中。
復(fù)合賦值符:+=,-=,|=等等,多使用可以使代碼更簡(jiǎn)潔。
5、單目操作符
??; ++; -- ; + ;-; ~ ;& ;* ;sizeof ; (類型)
- sizeof():判斷操作數(shù)的類型長(zhǎng)度,以字節(jié)為單位。
sizeof (int):必須加括號(hào);
sizeof x:可以不加括號(hào);
舉例:
16位機(jī)器中:int arry[10];
則sizeof(arry)=20
sizeof(arry[0])=2
sizeof(arry)/sizeof(arry[0])=10,即數(shù)組中元素的個(gè)數(shù)。
sizeof(a = b + 3):sizeof可以判斷表達(dá)式的長(zhǎng)度,此時(shí)不需要求表達(dá)式的值,所以a并沒有被賦值;表達(dá)式返回a的大小。
(類型):強(qiáng)制類型轉(zhuǎn)換(cast),具有很高的優(yōu)先級(jí),所以注意:把強(qiáng)制類型轉(zhuǎn)換放在表達(dá)式的前面只會(huì)改變第一個(gè)項(xiàng)目的類型,要操作整個(gè)表達(dá)式的話,就要加括號(hào)。
float(a):獲得a對(duì)應(yīng)的浮點(diǎn)數(shù)值。增值++和減值--操作符。
前綴操作符在變量被使用之前改變變量的值;
后綴操作符在變量被使用之后改變變量的值。
舉例:
c = ++a; d=a++;
其中,c得到a增加之前的值,d得到a增加之后的值。
前綴和后綴形式的增值操作都復(fù)制了一份變量值的拷貝,用于周圍表達(dá)式的值是這份拷貝來的值(賦值表達(dá)式);
前綴形式,在變量值增加之后復(fù)制,后綴形式,在變量值增加之前復(fù)制。
因此這些操作符的結(jié)果不是被他們修改的變量,而是變量值的拷貝。
++a = 10;
這條語句時(shí)錯(cuò)誤的,++a的結(jié)果是變量值的拷貝,不是變量a本身,因此無法向一個(gè)值進(jìn)行賦值。
6、關(guān)系操作符
>;>=;<;<=;!=;==
關(guān)系操作符的結(jié)果是一個(gè)整型值,而不是布爾值,可以將結(jié)果賦值給整型變量。(C語言中沒有布爾類型,所以用整數(shù)來代替,非0位真,0為假)
7、邏輯操作符
邏輯與&&,邏輯或||,都會(huì)控制表達(dá)式的順序執(zhí)行:
從左到右依次執(zhí)行,&&若左操作數(shù)為假,則后續(xù)右操作數(shù)不再求值,整個(gè)表達(dá)式為假,||邏輯或同理。這個(gè)行為被稱為“短路求值”(short-circuited evaluation)。
舉例:
if(a<b && c>d)
if(a<b & c>d)
和
if(a && b)
if(a & b)
第一組語句的結(jié)果一樣,因?yàn)殛P(guān)系操作符的結(jié)果只能是0或1,而第二組語句結(jié)果不一樣,因?yàn)槿鬭,b非0,則第一個(gè)一定為真,而a,b按位與的值并不一定非0,所以不一定為真。
8、條件操作符
expression1 ? expression2 : expression3
會(huì)控制子表達(dá)式的求值順序,expression2和expression3只會(huì)執(zhí)行其中一個(gè)。
b = a > 5 ? 3 : -20;
a > 5則b = 3,否則b = -20。
條件操作符的作用有時(shí)類似于if,else,且比它更簡(jiǎn)潔。
9、逗號(hào)操作符
expression1,expression2,expression3,......,expressionN
將多個(gè)表達(dá)式用逗號(hào)分開,并從左到右依次執(zhí)行,整個(gè)表達(dá)式的值為最后那個(gè)表達(dá)式的值。
while(a = get_value(), count_value( a ), a>0)
{
expression;
}
因?yàn)橐来螆?zhí)行,所以用在循環(huán)語句的測(cè)試表達(dá)式中時(shí),獲得下一個(gè)測(cè)試值語句只出現(xiàn)一次,修改時(shí)只需要在一個(gè)地方修改,方便程序的維護(hù)。
二、表達(dá)式求值
- 表達(dá)式的求值順序由所包含操作符的優(yōu)先級(jí)和結(jié)合性決定;
- 求值過程中的類型轉(zhuǎn)換。
1、隱式類型轉(zhuǎn)換(implicit conversion)
-
發(fā)生的情況:
- 算術(shù)表達(dá)式或邏輯表達(dá)式的操作數(shù)類型不相同時(shí);(執(zhí)行常用算術(shù)轉(zhuǎn)換,即usual arithmetic conversion)
- 操作符兩邊的變量類型不相同時(shí);
- 函數(shù)調(diào)用時(shí),實(shí)參與形參不匹配時(shí);
- return語句中表達(dá)式類型和函數(shù)返回值類型不匹配時(shí)。
隱式轉(zhuǎn)換規(guī)則
long double
double
float
unsigned long int
long int
unsigned int
int
排名較低的操作數(shù)首先轉(zhuǎn)換為另一個(gè)操作數(shù)的類型,即低精度數(shù)像高精度數(shù)轉(zhuǎn)換。
但是,在32位機(jī)器上,int類型和long字長(zhǎng)相同,這時(shí)unsigned int的精度就比long精度高。整型運(yùn)算符的精度至少是缺省整型類型,則運(yùn)算過程中,字符型和短整型在使用之前被轉(zhuǎn)換成普通整型,這種轉(zhuǎn)換過程被稱為整型提升(integral promotion)。
提升精度往往無害,但降低精度可能會(huì)導(dǎo)致問題,低精度類型可能不夠大,不能容納高精度的完整數(shù)據(jù)。
(1)算術(shù)轉(zhuǎn)換
舉例:
char a,b,c;
statement;
a = b + c;
首先,b和c的值被提升為整型,然后,執(zhí)行加法運(yùn)算,最后,將結(jié)果截短后賦值并存儲(chǔ)于a中。
當(dāng)然,也可以使用強(qiáng)制類型轉(zhuǎn)換執(zhí)行顯示轉(zhuǎn)換(explicit conversion):(int)a = b + c;
(2)顯示轉(zhuǎn)換
舉例:
int a = 5000;
int b = 25;
long c = a * b;
在32位機(jī)器上,int和long int都是32位,這段代碼運(yùn)行起來沒有問題;但在16位機(jī)器上,int是16位,long是32位,a*b的值應(yīng)該是int型,但由于數(shù)據(jù)類型不夠大(max = 2^16 - 1 = 65535),所以發(fā)生溢出,c會(huì)被賦一個(gè)無意義的值,因此需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換:
long c = (long)a*b;注意:
long c = (long)(a*b);是錯(cuò)誤的,因?yàn)橐绯鲈趶?qiáng)制轉(zhuǎn)換之前就已經(jīng)發(fā)生了,所以這樣做不會(huì)有任何改變。-
數(shù)據(jù)溢出:
- 有符號(hào)數(shù)的數(shù)據(jù)溢出是未定義的;
- 無符號(hào)數(shù)的數(shù)據(jù)溢出:溢出后的數(shù)以2^(8*sizeof(type))作模運(yùn)算。比如用unsigned char型變量存儲(chǔ)258,其實(shí)存進(jìn)去的是258-2^8=2。
(3)操作符兩邊表達(dá)式的轉(zhuǎn)換
舉例:
unsigned int a = 6;
int b = -20;
int c = (a + b) > 6 ? 1 : 2;
在加法運(yùn)算中a和b類型不一致,會(huì)發(fā)生隱式轉(zhuǎn)換,將int型轉(zhuǎn)換為unsigned int型,b=-20轉(zhuǎn)換為無符號(hào)數(shù)會(huì)變成一個(gè)很大的整數(shù),因?yàn)闊o符號(hào)負(fù)數(shù)轉(zhuǎn)換為的正數(shù)是用負(fù)數(shù)的補(bǔ)碼所表示的,正數(shù)的源碼,反碼,和補(bǔ)碼相同。所以,程序輸出1,而不是2。
舉例:
if(strlen(arry) < 10)
if(strlen(arry) - 10 < 0)
這兩條語句不等價(jià),因?yàn)閟trlen函數(shù)返回值是unsigned int型,兩個(gè)無符號(hào)數(shù)運(yùn)算所得的結(jié)果仍為無符號(hào)數(shù),而無符號(hào)數(shù)肯定大于0。所以,要盡量避免使用第二個(gè)表達(dá)式。
2、操作符的優(yōu)先級(jí)
表達(dá)式的運(yùn)算規(guī)則:
兩個(gè)相鄰操作符的執(zhí)行順序由他們的優(yōu)先級(jí)決定,若優(yōu)先級(jí)相同,則由他們的結(jié)合性決定。
結(jié)合性:當(dāng)多個(gè)相同優(yōu)先級(jí)的運(yùn)算符出現(xiàn)在表達(dá)式中時(shí),先執(zhí)行左邊的叫具有左結(jié)合性,先執(zhí)行右邊的叫具有右結(jié)合性。
- 舉例:
a*b + c*d + e*f
相鄰的加法和乘法運(yùn)算符中,乘法運(yùn)算符先執(zhí)行;兩個(gè)加法運(yùn)算,根據(jù)加法的左結(jié)合性,是左邊的加法運(yùn)算先執(zhí)行。但對(duì)于哪個(gè)乘法運(yùn)算先執(zhí)行,以及是否在所有乘法執(zhí)行完后再執(zhí)行加法運(yùn)算,這些都由編譯器決定,所以表達(dá)式就會(huì)有多種執(zhí)行順序。
c + --c
相鄰的+和--操作符是--先執(zhí)行,但對(duì)于表達(dá)式c和--c并不知道哪個(gè)先執(zhí)行,又因?yàn)?-c具有副作用,所以這兩個(gè)表達(dá)式的執(zhí)行順序會(huì)對(duì)結(jié)果有影響。
- 編譯器只要不違背優(yōu)先級(jí)和結(jié)合性規(guī)則,就可以任意決定復(fù)雜表達(dá)式的取值順序。