C Primer Plus學習筆記(四)- 運算符、表達式和語句
基本運算符
賦值運算符:=
在C語言中,=不是“相等”,而是賦值運算符,把左邊的值賦給右邊的變量
a = 2018; //把值2018賦給變量a
賦值表達式語句的目的是把值儲存到內存位置上,用於儲存值的數據存儲區域統稱為數據對象
加法運算符:+
將其左側的值與右側的值相加
減號運算符:-
將其左側的值減去右側的值
符號運算符:-和+
-作為一元運算符,改變其右側值的符號,一元運算符只需要一個運算對象
+作為一元運算符,不會改變運算對象的值或符號
除法運算符:/
/左側的值是被除數,/右側的值是除數
four = 12.0/3.0;
整數除法和浮點數除法不同。浮點數除法的結果是浮點數,整數除法的結果是整數
整數是沒有小數部分的數,在C語言中,整數除法結果的小數部分會被舍棄,這稱為截斷
整數除法會截斷計算結果的小數部分,而不是四舍五入
負整數的運算也是直接丟棄小數部分,這種方法被稱為“趨零截斷”
混合整數和浮點數計算的結果是浮點數
實際上,計算機不能真正用浮點數除以整數,編譯器會把兩個運算對象轉換成相同的類型
運算優先級順序:
運算的優先級順序跟數學運算優先級順序一樣
其他運算符
C語言規定,sizeof返回size_t類型的值,這是一個無符號整數類型,但它不是新類型
C有一個typedef機制,允許為現有類型創建別名
typedef double real; //創建一個double的別名real real test; //聲明一個double類型的變量test
C頭文件系統可以使用typeof把size_t作為unsigned int或unsigned long的別名。這樣在使用size_t類型時,編譯器會根據不同的系統替換標準類型
求模運算符:%
求模運算符用於整數運算
求模運算符給出其左側整數除以右側整數的余數
遞增運算符:++
遞增運算符將其運算對象遞增1,該運算符有兩種方式。
第一種是,++在其作用的變量前面,這是前綴模式
第二種是,++在其作用的變量後面,這是後綴模式
這兩種模式遞增行為發生的時間不同
x = a++; //後綴模式,使用了a的值之後才遞增a y = ++b; //遞增了b的值之後才使用b的值
還有遞減運算符,跟遞增運算符是一樣的
如果一個變量出現在一個函數的多個參數中,不要對該變量使用遞增或遞減運算符
如果一個變量多次出現在一個表達式中,不要對該變量使用遞增或遞減運算符
表達式和語句
表達式由運算符和運算對象組成,C語言中,每個表達式都有一個值,要想獲得這個值,就要根據運算符優先級順序規定的順序來執行操作
條件表達式的值為0或1,如果條件為真,表達式的值為1;如果條件為假,表達式的值為0
語句是C程序的基本構建塊,一條語句相當於一條完整的計算機指令,但不是所有的指令都是語句,例如x = 6 + (y * 5)
語句可以改變值或調用函數
在C中,大部分語句都以分號結尾
legs = 4 //只是一個表達式 legs = 4; //語句 ; //只有一個分號為空語句,最簡單的語句是空語句
C把末尾加上一個分號的表達式都看作是一條語句,即表達式語句,聲明不是表達式語句
#include <stdio.h> int main(void) { int count, sum; //聲明 count = 0; //表達式語句 sum = 0; //表達式語句 while (count++ < 20) //叠代語句 sum = sum + count; printf("sum = %d\n", sum); //表達式語句 return 0; //跳轉語句 }
如果刪除聲明後面的分號,剩下的部分不是一個表達式,也沒有值
int count //不是表達式,也沒有值
在C語言中,賦值和函數調用都是表達式,沒有所謂的“賦值語句”和“函數調用語句”,這些語句實際上都是表達式語句
賦值表達式語句在程序中很常用,它為變量分配一個值
賦值表達式語句的結構是,一個變量名,後面是一個賦值運算符,再跟著一個表達式,最後以分號結尾
函數表達式語句會引起函數調用
副作用和序列點
副作用是對數據對象或文件的修改,使用一條語句的主要目的就是使用其副作用
例如,調用printf()函數時,它顯示的信息其實是副作用
序列點是程序執行的點,在該點上,所有的副作用都在進入下一步之前發生
在C語言中,語句中的分號標記了一個序列點
在一條語句中,賦值運算符、遞增運算符和遞減運算符對運算對象做的改變必須在程序執行下一條語句前完成
任何一個完整表達式的結束也是一個序列點
完整表達式就是指這個表達式不是另一個更大表達式的子表達式
復合語句(塊)
復合語句是用花括號括起來的一條或多條語句,復合語句也稱為塊
#include <stdio.h> int main(void) { int count = 0; int sum = 0; while (count++ < 10) sum = 10 * count +2; printf("sum = %d\n", sum); }
while循環中只有一條賦值表達式語句,沒有花括號,while語句從while這行運行到下一個分號。循環結束後,才執行printf()函數
運行結果
#include <stdio.h> int main(void) { int count = 0; int sum = 0; while (count++ < 10) { sum = 10 * count +2; printf("sum = %d\n", sum); } }
花括號確保兩條語句都是while循環的一部分,每執行一次循環就調用一次printf()函數
根據while語句的結構,整個復合語句被視為一條語句
運行結果
類型轉換
當類型轉換出現在表達式時,無論是unsigned還是signed的char和short都會被自動轉換成int,如有必要會被轉換成unsigned int(如果short和int的大小相同,unsigned short就比int大,這種情況下,unsigned short會被轉換成unsigned int),從較小類型轉換成較大類型,這些轉換被稱為升級
涉及兩種類型的運算,兩個值會被分別轉換成兩種類型的更高級別
類型的級別從高到低依次是long double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。例外的情況是,當long和int的大小相同時,unsigned int比long的級別高。short和char類型會被升級到int或unsigned int
在賦值表達式語句中,計算的最終結果會被轉換成被賦值變量的類型。這個過程可能導致類型升級或降級。降級就是把一種類型轉換成更低級別的類型
當作為函數參數傳遞時,char和short被轉換成int,float被轉換成double
類型升級通常都不會有什麽問題,但是類型降級會導致一些麻煩,因為較低類型可能放不下整個數字
如果待轉換的值是否與目標類型匹配,這取決於轉換涉及的類型,待賦值的值與目標類型不匹配時,規則如下:
1.目標類型是無符號整型,且待賦的值是整數時,額外的位將被忽略
2.如果目標類型是有一個有符號整型,且待賦的值是整數,結果因實現而異
3.如果目標類型是一個整型,且待賦的值是浮點數,該行為是未定義的
如果把一個浮點數轉換成整數,小數部分會被截斷
強制類型轉換運算符
通常,應該避免自動類型轉換,尤其是類型降級
強制類型轉換,即在某個量的前面放置用圓括號括起來的類型名,該類型名即是希望轉換成的目標類型
圓括號和它括起來的類型名構成了強制類型轉換運算符
int mice; //定義一個int類型的變量mice mice = 1.6 + 1.7; //自動類型轉換,1.6+1.7的結果3.3被截斷為整數3 mice = int(1.6) + int(1.7); //1.6和1.7在相加之前被轉換成整型(1),把1+1的結果賦給mice
本質上,兩種類型轉換都好不到哪去,要考慮程序的具體情況再做取舍
帶參數的函數
看一個打印指定數量的#號的小程序
#include <stdio.h> void pound(int n); //ANSI函數原型聲明 int main(void) { int times = 5; char ch = ‘!‘; float f = 6.0f; pound(times); //int類型的參數 pound(ch); //和pound((int)ch);相同 pound(f); //和pound((int)ch);相同 return 0; } void pound(int n) //表示該函數接受一個int類型的參數 { while (n-- > 0) printf("#"); printf("\n"); }
運行結果
void pound(int n),如果函數不接受任何參數,函數頭的圓括號中應該寫上關鍵字void。由於該函數接受一個int類型的參數,所以圓括號中包含一個int類型變量n的聲明
聲明參數就創建了被稱為形式參數(形參)的變量
函數調用傳遞的值為實際參數,簡稱實參
形參是變量,實參是函數調用提供的值,實參被賦給相應的形參
函數的原型即是函數的聲明,描述了函數的返回值和參數,所以變量ch和f都被轉換成int類型
函數名前面有void關鍵字,該函數沒有返回值
C Primer Plus學習筆記(四)- 運算符、表達式和語句