1 位運算符
????????位運算符是對整數的二進制表示(補碼形式)進行逐位操作的運算符。以下是主要的位運算符及其功能描述:
運算符 | 描述 | 操作數個數 | 副作用 |
---|---|---|---|
& | 按位與 | 2 | 無 |
| | 按位或 | 2 | 無 |
^ | 按位異或 | 2 | 無 |
~ | 按位取反 | 1 | 無 |
<< | 按位左移 | 2 | 無 |
>> | 按位右移 | 2 | 無 |
1.1 按位與 &
- 運算規則:對于兩個數的每一位,如果兩個相應的位都為 1,則結果的該位為 1;否則,結果的該位為 0(遵循 “有 0 為 0,全 1 為 1” 規則)。
- 示例:5 & 3
- 5 的二進制表示:0101
- 3 的二進制表示:0011
- 按位與操作過程:
0 1 0 1
& 0 0 1 1 ------- 0 0 0 1結果:0001(十進制1)
1.2 按位或 |
- 運算規則:對于兩個數的每一位,如果兩個相應的位中至少有一個為 1,則結果的該位為 1;如果兩個相應的位都為 0,則結果的該位為 0(遵循 “有 1 為 1,全 0 為 0” 規則)。
- 示例:5 | 3
- 5 的二進制表示:0101
- 3 的二進制表示:0011
- 按位或操作過程:
0 1 0 1
| 0 0 1 1 ------- 0 1 1 1結果:0111(十進制7)
1.3 按位異或 ^
- 計算規則:對于兩個數的每一位,如果兩個相應的位不同(一個為 1,另一個為 0),則結果的該位為 1;如果兩個相應的位相同(都為 0 或都為 1),則結果的該位為 0。(遵循 “相同為 0,不同為 1” 規則)。
- 示例:5 ^ 3
- 5 的二進制表示:0101
- 3 的二進制表示:0011
- 按位異或操作過程:
0 1 0 1
^ 0 0 1 1 ------- 0 1 1 0結果:0110(十進制6)
????????為了清晰展示 8 位二進制數的位運算過程,我們可以使用 char?類型來進行按位與、按位或和異或操作的演示。由于 char?類型通常占用 8 位,非常適合作為位運算的示例載體。程序如下:
#include <stdio.h>int main()
{// 使用 char 類型(通常是 8 位)char a = 17; // 二進制表示為 0001 0001char b = -12; // -12 的補碼表示為 1111 0100// 按位與操作 &// 計算規則:【有 0 為 0,全 1 為 1】// 0001 0001 -> 17// &// 1111 0100 -> -12// 0001 0000 -> 16printf("a & b = %d\n", a & b); // 輸出為 16// 按位或操作 |// 計算規則:【有 1 為 1,全 0 為 0】// 0001 0001 -> 17// |// 1111 0100 -> -12// 1111 0101 -> -11printf("a | b = %d\n", a | b); // 輸出為 -11// 按位異或操作 ^// 計算規則:【相同為 0,不同為 1】// 0001 0001 -> 17// ^// 1111 0100 -> -12// 1110 0101 -> -27printf("a ^ b = %d\n", a ^ b); // 輸出為 -27return 0;
}
????????程序計算過程分析:
????????程序在 VS Code 中的運行結果如下所示:
1.4?按位取反 ~
- 計算規則:對一個數的二進制表示中的每一位(包括符號位)進行取反操作,即將所有的 0 變為 1,所有的 1 變為 0。
- 示例:例如,對于 char 類型(8 位)整數 17
- 其二進制表示為 0001 0001
- 按位取反后變為 1110 1110
- 結果為 -18
#include <stdio.h>int main()
{// 使用 char 類型(通常是 8 位)char a = 17; // 二進制表示為 0001 0001char b = -12; // -12 的補碼表示為 1111 0100// 按位取反操作 ~a// 0001 0001 -> 17// ~// 1110 1110 -> -18printf("~a = %d\n", ~a); // 輸出為 -18// 按位非操作 ~b// 1111 0100 -> -12// ~// 0000 1011 -> 11printf("~b = %d\n", ~b); // 輸出為 11return 0;
}
????????程序計算過程分析:
????????程序在 VS Code 中的運行結果如下所示:
1.5 按位左移 <<
-
功能:將一個數的二進制表示向左移動指定的位數。左移時,左側邊緣超出的位將被丟棄,而在右側邊緣新增的位將用 0 填充。
-
語法:a << b
- a?是要被左移的數。
- b?是指定左移的位數。
-
計算規則:
- 移動操作:將 a 的二進制表示(補碼)向左移動 b 位。
- 填充規則:
- 左側邊緣超出的位將被丟棄。
- 在右側邊緣新增的位用 0 填充。
-
注意事項:
- 未定義行為:如果指定左移的位數 b 是負數,則行為是未定義的(Undefined Behavior, UB)。
- 整數溢出:左移后的值可能超出該整數類型的表示范圍,導致整數溢出。
- 數學應用:左移操作通常用于將數乘以 2 的冪次方(假設沒有溢出)。
- 無符號整數:對于無符號整數,左移后的結果將保持為無符號數。
- 有符號整數:對于有符號整數,左移可能導致符號位的變化,進而影響整數的正負。例如,一個正數左移后符號位變為 1,那么結果將是一個負數。
-
示例:5 << 1
- 5 的八位二進制表示為:0000 0101
- 左移 1 位后為 0000 1010,即十進制數 10
- 左移 1 位相當于乘以 2 的 1 次方,即 5 * 2^1 = 10
#include <stdio.h>int main()
{// 演示未定義行為int a = 17;int undefined_shift = -1;int result = a << undefined_shift;printf("負數移位是未定義行為,示例: %d << %d = %d \n", a, undefined_shift, result); // 輸出可能是任意值// 演示數學應用int b = 5;// 左移操作通常用于將數乘以 2 的冪次方(假設沒有溢出)printf("%d << 4 = %d (5 * 2^4 = 80)\n", b, b << 4); // 輸出為 80// 演示無符號整數unsigned int e = 17;printf("無符號整數示例: %u << 2 = %u\n", e, e << 2); // 輸出為 68// 演示有符號整數int f = -12;printf("有符號整數示例: %d << 2 = %d\n", f, f << 2); // 輸出為 -48return 0;
}
????????程序計算過程分析:
????????程序在 VS Code 中的運行結果如下所示:
1.6 按位右移 >>
- 功能:將一個數的二進制表示向右移動指定的位數。右移時,右側邊緣超出的位將被丟棄,而左側邊緣新增的位根據整數的類型(有符號還是無符號)有不同的填充規則。
- 語法:a >> b
- a?是要被右移的數。
- b?是指定右移的位數。
- 計算規則:
- 移動操作:將 a 的二進制表示(補碼)向右移動 b 位。
- 填充規則:
- 右側邊緣超出的位將被丟棄。
- 無符號整數:表現為邏輯右移,即左側用 0 填充。
- 有符號整數:在左側邊緣新增的位的填充規則依賴于編譯器的實現,通常是算術右移,即用符號位填充,但這不是標準強制要求的。
- 算術右移 (Arithmetic Right Shift):
- 功能:在右移時,左側空出的位用符號位填充。
- 效果:保持數值的符號不變。
- 適用:用于有符號整數類型(如 int、char 等)。
- 示例:-10 >> 1
- -10 的二進制表示為 1111 0110(假設 8 位系統)
- 算術右移 1 位后為 1111 1011,即 -5
- 邏輯右移 (Logical Right Shift):
- 功能:在右移時,左側空出的位用 0 填充。
- 效果:不保持數值的符號,適用于無符號數。
- 適用:用于無符號整數類型(如 unsigned int、unsigned char 等)。
- 示例:10 >> 1
- 10 的二進制表示為 0000 1010
- 邏輯右移 1 位后為 0000 0101,即 5
- 注意事項:
- 未定義行為:如果指定右移的位數 b 是負數,則行為是未定義的(Undefined Behavior, UB)。
- 數學應用:對于無符號整數,右移操作通常用于將數除以 2 的冪次方(結果總是向下取整)。
- 實現依賴:對于有符號整數,C 標準沒有規定必須使用算術右移還是邏輯右移,這取決于編譯器的具體實現。
#include <stdio.h>int main()
{// 演示未定義行為int a = 256;int undefined_shift = -2;int result = a >> undefined_shift;printf("負數移位是未定義行為,示例: %d >> %d = %d \n", a, undefined_shift, result); // 輸出可能是任意值// 演示無符號整數的邏輯右移unsigned int b = 17;printf("無符號整數示例: %u >> 3 = %u\n", b, b >> 3); // 輸出為 2// 演示有符號整數的算術右移int c = -12;printf("有符號整數示例: %d >> 3 = %d\n", c, c >> 3); // 輸出為 -2// 演示算術右移和邏輯右移的區別unsigned int d = 128;printf("無符號整數(邏輯右移)示例: %u >> 1 = %u\n", d, d >> 1); // 輸出為 64int e = -128;printf("有符號整數(算術右移)示例: %d >> 1 = %d\n", e, e >> 1); // 輸出為 -64// 演示數學應用// 對于無符號整數,右移操作通常用于將數除以 2 的冪次方(結果總是向下取整)int f = 85;printf("%d >> 3 = %d (85 / 2^3 = 10)\n", f, f >> 3); // 輸出為 10 (85/8 = 10.625,向下取整為 10)// 對于有符號整數,右移操作的行為依賴于編譯器的實現// 需看編譯器使用算術右移還是邏輯右移int g = -80;printf("%d >> 3 = %d (-80 / 2^3 = -10)\n", g, g >> 3); // 輸出為 -10 (-80/8 = -10)return 0;
}
????????程序計算過程分析:
????????程序在 VS Code 中的運行結果如下所示:
1.7?位運算符的意義與應用?
????????在 C 語言中,位運算符(包括按位與 &、按位或 |、按位異或 ^、按位非 ~、左移 << 和右移 >>)提供了對整數類型底層二進制位的直接操作能力。這類操作在系統級或嵌入式編程中尤為重要,特別是在需要精確控制硬件寄存器、標志位或其他底層資源的場景中。
????????位運算的核心應用之一是對特定比特位的置位(設為 1)和復位(設為 0),而不會影響其他位的狀態。例如:
- 將第 n 位置為 1:使用按位或操作結合掩碼 (1 << n),即執行 x |= (1 << n);
-
(1 << n):
-
這是創建一個掩碼(mask),它的作用是生成一個只有第 n 位為 1,其余都為 0 的數。例如:
-
1 << 0 → 0b00000001 (第 0 位為 1)
-
1 << 1 → 0b00000010 (第 1 位為 1)
-
1 << 3 → 0b00001000 (第 3 位為 1)
-
-
注意:通常從右往左數位,最低位是第 0 位。
-
-
x | (1 << n):
-
用原來的值 x 和這個掩碼做按位或操作。
-
因為 OR 的規則是:只要有 1 就為 1。
-
所以無論原來第 n 位是 0 還是 1,經過 OR 后都會變成 1。
-
-
- 將第 n 位置為 0:使用按位與操作結合取反后的掩碼 ~(1 << n),即執行 x &= ~(1 << n);
-
(1 << n):
-
同上,先生成一個只有第 n 位為 1 的掩碼。
-
-
?~(1 << n):
-
對這個掩碼取反。原來是 0b00001000,取反后變成 0b11110111。
-
這樣我們就得到了一個除了第 n 位是 0,其他都是 1 的掩碼。
-
-
x & ~(1 << n):
-
將 x 和這個新掩碼進行按位與操作。
-
AND 的規則是:只要有一個是 0,結果就是 0。
-
所以:
-
第 n 位一定是 0(因為它和掩碼中的 0 做 AND)
-
其他位保持原樣(因為掩碼中是 1,所以保留原來的值)
-
-
-
????????這些操作允許程序員高效地修改寄存器中的某些位,從而實現對硬件狀態(如高低電平控制)的精細管理。由于位運算直接作用于數據的二進制表示,它們通常具有較高的執行效率,適合對性能敏感或資源受限的環境。
2 賦值運算符
????????在 C 語言中,賦值運算符用于將一個值賦予變量或表達式,它們是編程中非常基礎且重要的操作。以下是 C 語言中常見的賦值運算符及其用法。
運算符 | 描述 | 操作數個數 | 表達式的值 | 副作用 |
---|---|---|---|---|
= | 賦值 | 2 | 左邊操作數的值(賦值后的值) | 有,左邊操作數的值被更新 |
+= | 相加賦值 | 2 | 左邊操作數的值(相加后的值) | 有,左邊操作數的值被更新 |
-= | 相減賦值 | 2 | 左邊操作數的值(相減后的值) | 有,左邊操作數的值被更新 |
*= | 相乘賦值 | 2 | 左邊操作數的值(相乘后的值) | 有,左邊操作數的值被更新 |
/= | 相除賦值 | 2 | 左邊操作數的值(相除后的值) | 有,左邊操作數的值被更新 |
%= | 取余賦值 | 2 | 左邊操作數的值(取余后的值) | 有,左邊操作數的值被更新 |
<<= | 左移賦值 | 2 | 左邊操作數的值(左移后的值) | 有,左邊操作數的值被更新 |
>>= | 右移賦值 | 2 | 左邊操作數的值(右移后的值) | 有,左邊操作數的值被更新 |
&= | 按位與賦值 | 2 | 左邊操作數的值(按位與后的值) | 有,左邊操作數的值被更新 |
^= | 按位異或賦值 | 2 | 左邊操作數的值(按位異或后的值) | 有,左邊操作數的值被更新 |
|= | 按位或賦值 | 2 | 左邊操作數的值(按位或后的值) | 有,左邊操作數的值被更新 |
2.1 左值和右值
- 左值(Lvalue):左值是一個具有確定內存位置的變量或表達式,可以出現在賦值操作的左側。例如:變量名、數組元素、結構體成員等。
- 右值(Rvalue):右值是一個表示值的表達式,但沒有明確的內存位置。右值通常是一個常量、算術表達式的結果、函數調用返回的結果等。
2.2 注意事項
- 賦值運算符的第一個操作數(左值)必須是變量的形式,第二個操作數可以是任何形式的表達式。
- 左值必須可修改:賦值操作要求左側必須是一個左值,即一個可以存儲新值的內存位置。
- 右值提供值:賦值操作的右側可以是一個右值,它提供了要賦給左值的具體值。
-
示例:
- 正確用法:a = b + 25;(a 是左值,b + 25 是右值)
- 錯誤用法:b + 25 = a;(嘗試將右值 b + 25 用作左值,這是不允許的)
- 編譯錯誤結果,如下圖所示:
2.3 復合賦值運算符的使用
????????復合賦值運算符(如 +=、-= 等)的用法是將等號右邊的表達式首先計算為一個整體的值,然后將這個值與等號左邊的變量進行相應的運算,并將結果賦值回該變量。
a += b + 2;
等價于
a = a + (b + 2);
2.4 案例演示
#include <stdio.h>int main()
{int a = 10, b = 20, c = 30, d = 4;// 簡單的賦值a = 5;printf("a = %d\n", a); // a = 5// 加法賦值c += 3; // c = c + 3 = 30 + 3printf("c = %d\n", c); // c = 33// 減法賦值c -= b; // c = c - b = 33 - 20printf("c = %d\n", c); // c = 13// 乘法賦值a *= 2; // a = a * 2 = 5 * 2printf("a = %d\n", a); // a = 10// 除法賦值b /= 2; // b = b / 2 = 20 / 2printf("b = %d\n", b); // b = 10// 取模賦值c %= 3; // c = c % 3 = 13 % 3printf("c = %d\n", c); // c = 1// 連等寫法int e = 12, f;f = e *= a; // f = e * a = 12 * 10// 先計算 e *= a,然后將結果賦值給 fprintf("e = %d\n", e); // e = 120printf("f = %d\n", f); // f = 120// 左移賦值d <<= 2; // d = d << 2 = 4 << 2printf("d = %d\n", d); // d = 16// 右移賦值d >>= 1; // d = d >> 1 = 16 >> 1printf("d = %d\n", d); // d = 8// 按位與賦值a &= 1; // a = a & 1 = 10 & 1printf("a = %d\n", a); // a = 0// 按位異或賦值b ^= 3; // b = b ^ 3 = 10 ^ 3printf("b = %d\n", b); // b = 9// 按位或賦值b |= 4; // b = b | 4 = 9 | 4printf("b = %d\n", b); // b = 13return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
2.5?復合賦值運算符表達式的值
????????在 C 語言中,復合賦值運算符(如 +=、-=、*= 等)不僅簡化了賦值操作,還具有特定的表達式值。復合賦值運算符組成的表達式,其值為賦值后的左邊操作數的值。以下通過一個案例來演示這一特性:
#include <stdio.h>int main()
{int n = 5;// 使用復合賦值運算符 +=,并打印表達式的結果printf("n += 3 的結果是: %d\n", n += 3);// 執行過程:// 1. 計算 n + 3 = 8// 2. 將 8 賦給 n// 3. 表達式 n += 3 的值為賦值后的 n 的值,即 8// 輸出:n += 3 的結果是: 8// 驗證 n 的值printf("此時 n 的值是: %d\n", n); // 輸出:此時 n 的值是: 8return 0;
}
- 初始時,n 的值為 5。
- 執行 n += 3 時,先計算 n + 3 的值(即 8),然后將該值賦給 n。
- 表達式 n += 3 的值為賦值后的 n 的值,即 8,因此 printf 函數會輸出 8。
- 最后,再次打印 n 的值,確認 n 已經被更新為 8。
????????程序在 VS Code 中的運行結果如下所示:
2.6?連等寫法
????????C 語言支持連等寫法,即在一個表達式中連續進行多個賦值操作。連等寫法的賦值順序是從右向左進行的。以下通過一個案例來演示連等寫法:
#include <stdio.h>int main()
{int x, y;// 使用連等寫法 x = y = 99x = y = 99;// 執行過程解析:// 1. y = 99; 先執行,將 y 賦值為 99// 2. 然后 x = y; 執行,將 x 賦值為 y 的值,即 99// 打印 x 和 y 的值printf("x = %d, y = %d\n", x, y); // 輸出:x = 99, y = 99return 0;
}
- 在 x = y = 99; 這個連等寫法中,賦值操作是從右向左進行的。
- 首先,99 被賦給 y,此時 y 的值為 99。
- 然后,y 的值(即 99)被賦給 x,此時 x 的值也為 99。
- 最終,printf 函數輸出 x = 99, y = 99,驗證了連等寫法的執行順序和賦值過程。
????????程序在 VS Code 中的運行結果如下所示:
3 三元運算符
3.1 基本語法
????????三元運算符(也稱為條件運算符)的基本語法如下:
條件表達式 ? 表達式1 : 表達式2;
- 條件表達式:一個邏輯表達式,用于判斷其真假。
- 表達式1:如果條件表達式為真(非 0),則整個三元運算符的值為表達式 1 的值。
- 表達式2:如果條件表達式為假(0),則整個三元運算符的值為表達式 2 的值。
3.2?案例演示
#include <stdio.h>int main()
{int a = 99;int b = 99;// 使用三元運算符來決定 res 的值int res = a > b ? a++ : b--;// 因為 a 不大于 b,所以執行 b--,并將結果賦值給 res// 注意:b-- 是后綴自減,意味著先返回 b 的當前值(99),然后再將 b 減 1// res 被賦值為 99,然后 b 變為 98printf("a=%d\n", a); // 輸出 a=99,因為 a 沒有被改變printf("b=%d\n", b); // 輸出 b=98,因為 b 在前面的條件運算符中自減了printf("res=%d\n", res); // 輸出 res=99,因為 res 被賦值為 b 自減之前的值float n1 = a > b ? 1.1 : 1.2; // 條件表達式為真,整個表達式的值是表達式1:1.1// 注意:由于 b 在前面的條件運算符中已經被自減,所以這里 b 的值是 98printf("n1=%.2f\n", n1); // 輸出 n1=1.10return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
3.3?案例:計算兩個數的最大值
#include <stdio.h>int main()
{int a = 10;int b = 100;// 使用三元運算符計算 a 和 b 中的最大值int max = a > b ? a : b;printf("a 和 b 中最大的數字:%d", max); // 輸出:a 和 b 中最大的數字:100return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
3.4?案例:計算三個數的最大值
#include <stdio.h>int main()
{int a = 10;int b = 100;int c = 199;// 1. 使用兩次三元運算符來計算 a、b 和 c 中的最大值int max_a_b = a > b ? a : b;int max_a_b_c = max_a_b > c ? max_a_b : c;// 輸出 a、b 和 c 中最大的數字printf("a、b、c 中最大的數字:%d\n", max_a_b_c); // 輸出:a、b、c 中最大的數字:199// 2. 使用嵌套的三元運算符來找出 a、b、c 中的最大值// 首先比較 a 和 b,然后將較大的值與 c 比較int max = (a > b ? a : b) > c ? (a > b ? a : b) : c;// 輸出 a、b 和 c 中最大的數字printf("a、b、c 中最大的數字:%d\n", max); // 輸出:a、b、c 中最大的數字:199// 3. 也可以簡化為下面的寫法:max = (a > b ? (a > c ? a : c) : (b > c ? b : c));// 輸出 a、b 和 c 中最大的數字printf("a、b、c 中最大的數字:%d\n", max); // 輸出:a、b、c 中最大的數字:199// 4. 也可以使用 if-else 語句來找出 a、b、c 中的最大值// 后續在學習 if-else 語句時會詳細講解return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
4 逗號運算符
4.1 基本語法
????????逗號運算符(,)是 C 語言中一個簡單但強大的運算符,用于將多個表達式連接在一起。它的基本語法如下:
表達式1, 表達式2, 表達式3, ..., 表達式N;
- 表達式1, 表達式2, ..., 表達式N:這些是逗號運算符連接的多個表達式。
4.2 表達式的值
????????逗號運算符的特性如下:
- 求值順序:逗號運算符從左到右依次計算每個表達式的值。
- 表達式的值:整個逗號表達式的值為最后一個表達式的值。
4.3 案例演示
#include <stdio.h>int main()
{int a = 5, b = 10, c = 15;// 使用逗號運算符依次執行多個表達式int result = (a = b, b = c, c = a + b);// 執行過程解析:// 1. a = b,將 b 的值賦給 a,此時 a = 10, b = 10, c = 15// 2. b = c,將 c 的值賦給 b,此時 a = 10, b = 15, c = 15// 3. c = a + b,將 a 和 b 的和賦給 c,此時 a = 10, b = 15, c = 25// 最后,result 被賦值為 c 的值,即 25// 打印結果printf("a = %d, b = %d, c = %d\n", a, b, c); // 輸出:a = 10, b = 15, c = 25printf("result = %d\n", result); // 輸出:result = 25return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
4.4 使用場景
????????逗號運算符通常用于以下場景:
- 簡化代碼:在需要執行多個操作但不想使用多行代碼時,可以使用逗號運算符。
- 循環和條件語句:在 for 循環的初始化或更新部分,或者在需要多個條件判斷的地方,可以使用逗號運算符。
4.5 注意事項
- 可讀性:雖然逗號運算符可以簡化代碼,但過度使用可能會降低代碼的可讀性。
- 優先級:逗號運算符的優先級較低,因此在復雜的表達式中,建議使用括號來明確運算順序。
5 運算符優先級
5.1 運算符優先級表格說明
優先級 | 運算符 | 名稱或含義 | 結合方向 |
---|---|---|---|
1 | [] | 數組下標 | 左到右 |
() | 函數調用、圓括號 | ||
. | 成員選擇(對象) | ||
-> | 成員選擇(指針) | ||
2 | -(單目) | 負號運算符 | 右到左 |
(類型) | 強制類型轉換 | ||
++ | 自增運算符 | ||
-- | 自減運算符 | ||
*(單目) | 取值運算符(解引用) | ||
&(單目) | 取地址運算符 | ||
! | 邏輯非運算符 | ||
~ | 按位取反運算符 | ||
sizeof | 長度運算符 | ||
3 | / | 除 | 左到右 |
*(雙目) | 乘 | ||
% | 余數(取模) | ||
4 | + | 加 | 左到右 |
-(雙目) | 減 | ||
5 | << | 左移 | 左到右 |
>> | 右移 | ||
6 | > | 大于 | 左到右 |
>= | 大于等于 | ||
< | 小于 | ||
<= | 小于等于 | ||
7 | == | 等于 | 左到右 |
!= | 不等于 | ||
8 | & | 按位與 | 左到右 |
9 | ^ | 按位異或 | 左到右 |
10 | | | 按位或 | 左到右 |
11 | && | 邏輯與 | 左到右 |
12 | || | 邏輯或 | 左到右 |
13 | ?: | 條件運算符 | 右到左 |
14 | = | 賦值運算符 | 右到左 |
/= | 除后賦值 | ||
*= | 乘后賦值 | ||
%= | 取模后賦值 | ||
+= | 加后賦值 | ||
-= | 減后賦值 | ||
<<= | 左移后賦值 | ||
>>= | 右移后賦值 | ||
&= | 按位與后賦值 | ||
^= | 按位異或后賦值 | ||
|= | 按位或后賦 | ||
15 | , | 逗號運算符 | 左到右 |
- 優先級記憶:雖然運算符優先級表看起來復雜,但通常不需要刻意記憶所有優先級。一般來說,一元運算符(如 !、-(單目)、++、-- 等)的優先級高于算術運算符,算術運算符的優先級高于關系運算符,關系運算符的優先級高于邏輯運算符,邏輯運算符的優先級高于三元運算符,三元運算符的優先級高于賦值運算符。
- 常用優先級關系:自增自減 >?邏輯非 ! > 算術運算符 > 關系運算符 > 邏輯與 && > 邏輯或 || > 條件運算符 ?: > 賦值運算符。
- 使用括號:如果對運算符的優先級不確定,或者為了代碼的可讀性,可以直接使用括號來明確運算順序。括號可以覆蓋任何運算符的優先級,確保表達式按照預期的方式計算。
5.2 復雜表達式的計算分析
表達式 5 > 3 && 8 < 4 - !0 的計算過程分析
????????問題:對于表達式 5 > 3 && 8 < 4 - !0 的最終值是多少?計算過程是怎樣的?
????????計算過程:
- 短路性質:首先計算 && 左邊的表達式 5 > 3,其邏輯值為 1。
- 右邊表達式:計算 && 右邊的表達式 8 < 4 - !0。
- 非運算:!0 的邏輯值為 1。
- 算術運算:4 - 1 的結果為 3。
- 關系運算:8 < 3 的邏輯值為 0。
- 邏輯與運算:整個表達式的值為 1 && 0,最終邏輯值為 0。
表達式 a + b < c && b == c && a || b + c && b + c 的計算過程分析
????????問題:若 a = 2,b = 3,c = 4,表達式 a + b < c && b == c && a || b + c && b + c 的計算過程是怎樣的?值為多少?
????????計算過程:
- 短路性質:首先計算 a + b < c,即 2 + 3 < 4,其邏輯值為 0。
- 短路效果:b == c && a 不會執行,|| 左邊的值為 0。
- 右邊表達式:計算 b + c && b + c。
- 算術運算:b + c 的結果為 7。
- 邏輯與運算:7 && 7 的邏輯值為 1。
- 邏輯或運算:0 || 1 的邏輯值為 1。
表達式 (m = a > b) && (n = c > d) 的計算過程分析
????????問題:設有 int a = 1, b = 2, c = 3, d = 4, m = 2, n = 2;執行 (m = a > b) && (n = c > d) 后 m 和 n 的值分別是多少?表達式的結果是多少?
????????計算過程:
- 短路性質:首先計算(m = a > b)。
- 關系比較:a > b,即 1 > 2,其邏輯值為 0。
- 賦值運算:m = 0。
- 短路效果:(n = c > d) 不執行。
- 最終結果:
- m 的值為 0。
- n 的值為 2。
- 整個表達式的結果為 0。
總結與建議:
- 避免復雜表達式:盡量簡化表達式。
- 明確執行順序:使用小括號明確執行順序,避免依賴運算符優先級。
- 拆分表達式:復雜表達式拆分為多個步驟,提高可讀性和可維護性。
6 編程練習
6.1?會員折扣
????????某商店對會員提供折扣:
- 消費滿 100 元,打 9 折
- 消費滿 200 元,打 8 折
- 否則,無折扣
????????編寫一個程序,根據消費金額計算最終價格。
#include <stdio.h>int main()
{float amount; // 消費金額printf("請輸入消費金額:");scanf("%f", &amount); // 輸入消費金額float final_amount = (amount >= 200) ? amount * 0.8 : (amount >= 100) ? amount * 0.9: amount;printf("最終價格:%.2f 元\n", final_amount);return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
6.2 多任務處理
????????假設你在管理一個任務列表,每個任務有 ID、優先級和狀態。編寫一個程序,更新任務的狀態和優先級,并輸出更新后的信息。
#include <stdio.h>int main()
{int task_id = 101, priority = 3, status = 0;// 更新任務優先級和狀態int updated = (priority = 5, status = 1);printf("任務 ID:%d,優先級:%d,狀態:%d\n", task_id, priority, status);return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
6.3?燈的開關狀態
????????你有一個 8 位的整數表示 8 個燈的狀態(1 表示開,0 表示關)。初始狀態為所有燈關閉。編寫一個程序,打開第 2 和第 7 個燈,并關閉第 4 個燈(如果它是開的)。
#include <stdio.h>int main()
{unsigned char lights = 0b00000000; // 所有燈關// 打開第 2 和第 7 個燈lights |= (1 << 1) | (1 << 6);// 關閉第 4 個燈(如果它是開的)lights &= ~(1 << 3);return 0;
}
6.4?權限檢查
????????在一個系統中,權限使用一個整數的二進制位表示。例如,0000 1010 表示用戶有權限 2 和 4(從右到左,最低位為權限 1)。編寫一個程序,給用戶添加權限 3,并檢查用戶是否有權限 4。
#include <stdio.h>int main()
{unsigned char permissions = 0b00001000; // 初始權限// 添加權限 3permissions |= (1 << 2);// 檢查是否有權限 4int has_permission_4 = (permissions & (1 << 3)) != 0;// 解釋上面這行代碼// (permissions & (1 << 3)) != 0// permissions & (1 << 3) 是將 permissions 的第 3 位與 1 進行按位與操作,如果第 3 位為 1,則結果為 1,否則為 0。// != 0 是判斷結果是否不等于 0,如果不等于 0,則表示第 3 位為 1,即有權限 4,否則沒有權限 4。// 加上 != 0 使得代碼更加規范,因為 (permissions & (1 << 3)) 的結果是一個整數,而不是一個布爾值。printf("用戶是否有權限 4?%s\n", has_permission_4 ? "有" : "無");return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
6.5?溫度更新
????????假設你有一個初始溫度值為 25 攝氏度。由于天氣變化,溫度變化如下:
- 上午升溫 3 度。
- 下午降溫 2 度。
- 晚上升溫 1 度。
????????編寫一個程序,計算晚上的最終溫度。
#include <stdio.h>int main()
{float temperature = 25;temperature += 3; // 上午升溫temperature -= 2; // 下午降溫temperature += 1; // 晚上升溫printf("晚上的最終溫度:%.2f 攝氏度\n", temperature);return 0;
}
????????程序在 VS Code 中的運行結果如下所示: