二、常量(瞭解)
常量就是程式執行過程中不能改變的量,C語言中常量有:字面值常量、宏常量、列舉常量。
字面值常量
100 int
100l long
100ll long long
100u unsigned int
100lu unsigned long
100llu unsigned long long
定義一個宏常量表示100年總共有多少秒,不考慮閏平年
#defined SEC 3600*24*365*100u
3.14 double
3.14f float
3.14F long double
'b' char
注意:使用適當的字尾可以確保資料型別的匹配,其次提高程式碼可讀性,如果沒有正確的字尾,可能導致不正確的結果或者型別轉換的問題
三、格式化輸出
%nd 最少顯示n個字元寬度,不夠則補空格,右對齊
%-nd 最少顯示n個字元寬度,不夠則補空格,左對齊
%0nd 最少顯示n個字元寬度,不夠則補0,右對齊
%n.mf 最少顯示n個字元寬度(包括小數點),小數點後顯示m個字元寬度,不夠則補空格,右對齊
%g 不顯示小數點後多餘的0
四、運算子
自變運算子
前自變:++num/--num
變數的值立即加1或者減1
後自變:num++/num--
變數的值也會加1或減1,但是在下一行程式碼才生效
注意:
不要在一行程式碼中過多地使用自變運算子,因為不同的編譯器對它們的解釋規則不同,而且有時候會在合適的地方把後自變最佳化成前自變
只能給變數使用自變運算子
算術運算子
+ - *
/ 除 進行除法運算,獲取商
% 求餘 進行除法運算,獲取餘數
注意:
整數/整數 計算結果沒有小數部分 例如5/3 1 3/5 0
/和%都是除法運算,除數不能為0,執行時會出現 浮點數例外 (核心已轉儲) 的報錯資訊,並且程式立即停止執行
求餘的運算物件不能出現浮點數
關係運算子
>
>=
<
<=
== 相等
!= 不相等
注意1:它們的運算結果是邏輯值,C語言中的邏輯值用0(假)和1(真)來模擬的,並且計算出來的結果還可以繼續參與數學運算
注意2:
與數學規則不同 ,10 < num <100 在C語言中會先計算左邊小於號,得到邏輯值1\0,該結果繼續與100進行比較,一定滿足小於100,所以在C中是恆為真的。
注意3:
使用 == 運算子時,容易漏寫一個= ,變成賦值,所以一般把常量放在==的左邊,如果漏寫,編譯器會報錯
邏輯運算子
會把運算物件先轉換成邏輯值再運算,值為0時邏輯為假,值非0時邏輯為真
A && B 邏輯與運算子
一假即假
A || B 邏輯或運算子
一真即真
!A 邏輯非運算子
求反
注意:! 運算子的優先順序要比 && || 高
&& || 短路特性:
當左邊的運算結果已經可以確定整個邏輯運算表示式的結果時,右邊的不再進行執行
適當地使用該特性可以精簡if單分支結構,能看懂即可,不要太過分,不要過分地影響程式碼可讀性
if(num > 0) {
num++;
}
等同於
((num > 0) && (num++));
三目運算子
[A] ? [B] : [C]
有三個運算物件,先把A轉換成邏輯值,為真則執行B,為假則執行C,相當於精簡版的if else結構
注意:與else if不同的是三目運算子必須有運算結果,所以裡面不能出現流程控制語句:return 0\break\continue
賦值運算子
= 注意:賦值的值就是整個賦值運算子的運算結果
a += b; a = a+b;
a -= b; a *= b; a/=b; a%=b;
sizeof位元組運算子:
sizeof不是函式,是C語言32個關鍵字之一,能計算出資料在記憶體中的所需要的位元組數,如果運算物件不是一個表示式時,可以不使用小括號 sizeof num
並且sizeof括號內的表示式沒有執行,只是猜測裡面位元組數最大為結果`
sizeof(10>100?3.13:40) // 結果是8 並沒有執行三目運算子
位運算子
& | ~ ^ >> << 都是針對資料的二進位制補碼進行運算,後序再詳細講解
五、型別轉換
前提:只有相同型別的資料才能在一起進行運算,因為不同型別的資料,位元組數不同、格式、運算規則不同,必須把不同型別的資料轉換成同一型別才能運算。
自動型別轉換(隱式型別轉換):
不同型別的資料組成的表示式,編譯器會把先它們轉換成相同的型別再計算,這叫作自動型別型別或隱式型別轉換,它們的轉換規則是以不丟失資料為前提:在C語言中,當不同的基本型別組成表示式時,會發生自動型別轉換。這些轉換規則通常是為了使表示式能夠正確計算。以下是一些基本型別在表示式中自動型別轉換的規則:
-
整數提升:在表示式中,所有的char和short型別的值都會被提升為int型別。
-
算術轉換:算術轉換髮生在不同數值型別之間。具體來說,當一個運算元是浮點數(float或double)而另一個運算元是整數(int、char、short等)時,整數會被轉換為浮點數。這種轉換確保了浮點數和整數之間進行運算時的精度。
-
無符號整數和有符號整數的轉換:當無符號整數和有符號整數進行運算時,有符號整數的型別會被提升為與其範圍相同的無符號整數型別。
-
賦值運算子(=):在賦值運算子中,右邊的表示式會被轉換為左邊變數的型別。
這些規則是為了使C語言在處理不同型別的資料時能夠正確地計算結果。然而,需要注意的是,過多的型別轉換可能會導致程式碼難以理解和維護,因此在編寫程式碼時需要小心處理這些轉換。
強制型別轉換(顯式型別轉換):
在C語言中,強制型別轉換(也稱為顯式型別轉換)用於將一個表示式或變數的值強制轉換為指定的型別。強制型別轉換的語法如下:
(type) expression
其中,type
是目標型別,expression
是要轉換的表示式或變數名。
請注意以下幾點關於強制型別轉換的注意事項:
- 強制型別轉換可以將表示式或變數的值轉換為目標型別,無論其原始型別是什麼。這可能導致精度損失或資料截斷(位元組多的向位元組數少進行轉換 )。
- 強制型別轉換隻改變值的解釋方式,而不改變任何位的內容。例如,將一個浮點數轉換為整數時,小數部分會被丟棄。
- 當進行強制型別轉換時,應確保轉換操作是合法和安全的。例如,在將浮點數轉換為整數時,如果浮點數超出了整數型別的表示範圍,則結果可能是不確定的。
- 強制型別轉換可以應用於基本型別、指標型別和列舉型別。
以下是一些示例,展示了強制型別轉換的使用情況:
int a = 10;
double b = 3.14;
int result = (int) b; // 將浮點數轉換為整數型別
double sum = (double) a + b; // 將整數轉換為浮點數型別
int* ptr = (int*) malloc(sizeof(int)); // 強制將返回型別為 void* 的 malloc 轉換為 int* 型別
enum Colors { RED, GREEN, BLUE };
int color = (int) GREEN; // 將列舉型別轉換為整數型別
需要謹慎使用強制型別轉換,確保轉換操作的安全性和合理性。不正確的型別轉換可能會導致程式執行時錯誤和不確定的行為。應仔細考慮使用強制型別轉換的場景,並儘量避免過度依賴強制型別轉換來解決問題,而是考慮是否有更好的設計和型別匹配的方案。
六、if語句
程式碼的預設執行流程是從上到下,逐步、逐條執行的,if語句可以根據判斷條件選擇讓程式碼是否執行,改變了程式碼 的預設執行流程,所以這種語句也叫流程控制語句。
if(條件) { // 單分支
// 當條件為真時,執行此處程式碼,如果此處程式碼只有一行,大括號可以省略,但在商業專案中建議不要省略,因為這樣會影響程式碼的可擴充套件性
}
if(條件) { // 雙分支
// 當條件為真時,執行此處程式碼
} else {
// 當條件為假時,執行此處程式碼
}
if(條件1) { // 多分支
// 當條件1為真時,執行此處程式碼
} else if(條件2) { // 可以有多個else if
// 當條件2為真時,執行此處程式碼
}
...
else {
// 當條件1、條件2都為假時,執行此處程式碼
}
注意:
如果if、else的程式碼塊只有一行程式碼,大括號可以省略。
儘管C語言允許在if語句中省略大括號,但這樣做可能導致以下問題:
- 可讀性差:如果省略大括號,只有緊隨其後的一行程式碼會在if條件為真時執行。這樣容易導致程式碼混淆,特別是當if語句中有多行程式碼時,很難一目瞭然地知道哪些程式碼受到if條件的控制。
- 容易出錯:因為只有緊隨其後的一行程式碼受到if條件的控制,如果在if語句後面新增其他程式碼而沒有新增大括號,則新新增的程式碼無論if條件真還是假都會執行。這可能會導致邏輯錯誤和意外行為。
- 可維護性低:如果要在if條件為真時新增更多程式碼,需要手動新增大括號。在多個if語句巢狀時,容易忘記新增大括號或新增錯誤的大括號,導致邏輯混亂和錯誤。
因此,為了保證程式的可讀性、可靠性和可維護性,推薦始終使用大括號明確指定if語句的程式碼塊。即使if條件只控制一行程式碼,也應該用大括號將該行程式碼包裹起來,以防止潛在的錯誤和增加程式碼的可讀性。
七、switch開關語句
switch(資料) {
case v1: 語句1; break;
case v2: 語句2; break;
case v3: 語句3; break;
...
default: 語句4;
}
1、switch小括號中的資料可以是變數、表示式、常量,但是結果一定是整型;case後面的資料也必須是整型常量,不能是變數
2、當case後面的值與資料相等時會開啟開關,case後面的語句會執行,如果開關沒有透過break關閉,那會一直往下執行
3、default無論放在switch中哪個位置,當所有的case開關都不滿足時,會最後執行dafault的內容
4、switch與if else 比較只是程式碼較為簡潔,switch能解決的問題,if else一樣可以解決,所以在實際開發中程式設計師一般只使用if else就足夠了
gnu編譯器的專用語法:
switch(資料) {
case n1 ... n2: 語句; break;
}
表示 [n1,n2]之間都滿足開啟開關
八、for迴圈語句
透過反覆執行一段程式碼,達到解決問題的目的,被反覆執行的程式碼稱為迴圈語句
for是一種非常靈活的迴圈,一般使用一個變數來引導它的執行,該變數稱為迴圈變數,早期使用index名字作為迴圈變數名,後面逐漸演變成i,如果有多個迴圈巢狀時,可以使用i j k l
for ([1]; [2]; [3]) {
[4];
}
模組1:為for迴圈做一些準備工作:定義迴圈變數、給迴圈變數賦初值,但是在for中定義的變數,一旦出了迴圈就無法使用
模組2:判斷迴圈條件是否成立,如果條件成立執行模組4,如果條件不成立則結束for迴圈,如果沒有語句,則預設條件成立
模組4:被反覆執行的程式碼,稱為迴圈體語句
模組3:改變迴圈變數的值,讓迴圈變數自加或自減,i++、i--,防止出現死迴圈
迴圈流程:
1、2、4、3、2、4、3、2、4、3、4...
C89語法標準下不允許在模組1中定義變數,C99之後允許定義,當前Ubuntu16.04預設的gcc語法標準採用的是c99標準
如果想要使用其他標準:
gcc xxx.c -std=gnu89\99\11
for的不同寫法:
for (;;) { // 死迴圈
...
}
int i=0;
for (; i < 10; ++i) {
...
}
for(int i = 0; i < 10; ) {
...
i++;
}
for (int i = 0; i < 10; ++i,...);
注意:如果無論for迴圈寫成什麼樣子,迴圈體只執行1次,就很有可能是小括號後面加了分號
注意:
1、建議for的大括號要上下對齊,裡面的內容要縮排一次
2、如果for迴圈只有一行程式碼,可以省略大括號,但是商業程式碼要求不能省略
九、while迴圈語句
while (迴圈條件) {
// 迴圈語句
}
for (;迴圈條件;) {
// 與上面的while效果一致
}
執行流程:先檢查迴圈條件,條件為真執行迴圈體,條件為假,直接結束while迴圈
for迴圈的精簡版本效果與while相似
當明確知道迴圈次數的問題適合使用for迴圈解決
當不明確知道迴圈次數的問題,適合使用while迴圈解決
十、do-while迴圈語句
do {
// 迴圈語句
} while (迴圈條件); // 分號不能少
執行流程:先執行迴圈語句,再判斷迴圈條件,條件為真執行迴圈體,條件為假,直接結束迴圈,無論條件真或假,迴圈語句必定最少執行一次
注意:適合先幹活、再判斷的場景:輸入密碼判斷
注意:在do-while的大括號中定義變數,在小括號中不能使用
十一、迴圈巢狀
迴圈語句中包含了迴圈語句,特點:外層迴圈執行1次,內層迴圈執行n次
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
}
printf("\n");
}
十二、跳轉語句
break跳轉語句
用法1:在switch語句中可以關閉開關
用法2:在迴圈語句中,可以跳出所在的一層迴圈,是一種提前結束迴圈的一種方式,提高迴圈效率
continue跳轉語句
只能在迴圈語句中,停止本次迴圈,直接進入下一次迴圈,根據條件改善迴圈的執行
return跳轉語句
return可以提前結束函式,並返回一個結果給呼叫者
goto跳轉語句
標籤名:
...
goto 標籤名;
goto可以在函式內任意跳轉,但是由於它過於靈活自由,可能會破壞已經設計好的分支、迴圈語句,因此一般公司都禁止使用goto,新的程式設計語句中已經取消了該關鍵字
goto非常適合驅動程式中處理異常、釋放資源,所以在硬體程式設計中適合使用