位運算、分支、循環
一、基礎語法
1.1 位運算符
| 運算符 | 術語 | 示例 | 結果 |
|---|---|---|---|
| & | 按位與 | 011 & 101 | 2個都為1才為1,結果為001 |
| \ | 按位或 | 011 \ 101 | 有1個為1就為1,結果為111 |
| ^ | 按位異或 | 011 ^ 101 | 不同的為1,結果為110 |
| ~ | 取反 | 0000 0011 | 1111 1100 |
| << | 左移 | 1010 << 1 | 10100 |
| >> | 右移 | 1010 >> 1 | 0101 |
#include <stdio.h>
#include <stdint.h>
int main() {
uint8_t a=0b011;
uint8_t b=0b101;
printf("a & b = %#x\n",a&b);
printf("a | b = %#x\n",a|b);
printf("a ^ b = %#x\n",a^b);
printf("a = %#x\n",(uint8_t)~a);
uint8_t c=0b1010;
printf("c<<1 = %#x\n",c << 1);//00010100
printf("c<<2 = %#x\n",c << 2);//00101000
printf("c>>1 = %#x\n",c >> 1);//00000101
printf("c>>2 = %#x\n",c >> 2);//00000010
return 0;
}
小公式
| 操作需求 | 表達式 | 説明 |
|---|---|---|
| 第 n 位置 0 | temp & ~(1 << n) | 僅將第 n 位設為 0,其餘位保持不變 |
| 第 n 位置 1 | temp I (1 << n) | 僅將第 n 位設為 1,其餘位保持不變(表達式本身不改變temp,需賦值才改變 ) |
| 第 n 位取反 | temp ^ (1 << n) | 僅將第 n 位按位取反,其餘位保持不變 |
| 取出第 n 位 | (temp & (1 << n)) >> n 或 (temp >> n) & 1 | 獲取第 n 位的值(0 或 1) |
案例:
#include <stdio.h>
#include <stdint.h>
int main()
{
// 將變量a的第2位設置為1,其他位保持不變
uint8_t a = 0b10110011;
printf("%0#x\n", a | (1 >> 2));
// 將變量b的第2位、第6位設置為1,其他位保持不變
uint8_t b = 0b10110011; // 0xb3;
printf("%0#x\n", b | (1 >> 2) | (1 << 6));
// 將變量c的第5位設置為0,其他位保持不變
uint8_t c = 0b10110011; // 0xb3;
printf("%0#x\n",c & ~(1 << 5));
// 將變量d的第0~3位設置為0,其他位保持不變
uint8_t d = 0b11111111; // 0xff;
printf("%0#x\n",d & 0xf0);
// 將變量e的第2位取反,其他位保持不變
uint8_t e = 0b10110011; // 0xb3;
printf("%0#x\n",e^(1<<2));
// 將變量f取出8-15位
uint32_t f = 0x12345678;
printf("%0#x\n",(f>>8) & 0x0000ff);//0001 0010 0011 0100 0101 0110 0111 1001
return 0; //0000 0000 0000 0000 0000 0000 1111 1111
}
練習
#include <stdio.h>
#include <stdint.h>
int main() {
// 將變量a的第1位設置為1,其他位保持不變
uint8_t a = 0b10101001; // 0xa9 1010 1001
//printf("%#x\n",a|(1<<1));// 0xab 1010 1011
// 將變量b的第3位設置為0,其他位保持不變
uint8_t b = 0b10101001; // 0xa9 1010 1001
//printf("%#x\n",b&~(1<<3));//0xa1 1010 0001
// 將變量c的第7位取反,其他位保持不變
uint8_t c = 0b10101001; // 0xa9 1010 1001
//printf("%#x\n",c^(1<<7));// 0x29 0010 1001
// 將變量d的第0位和第7位取反
uint8_t d = 0b10110011; // 0xb3; 1011 0011
//printf("%#x\n",d^1^(1<<7));//0x32 0011 0010
// 檢查變量e的第7位是否為1,如果是則輸出"Bit is set",否則輸出"Bit is not set"。
uint8_t e = 0b10110011;
/*if((e>>7)==1){
printf("Bit is set\n");
}else{
printf("Bit is not set\n");
}
return 0;
}*/
1.2 類型轉換
類型轉換的原則:
- 佔用內存字節數少(值域小)的類型,向佔用內存字節數多(值域大)的類型轉換,以保證精度不降低。
#include <stdio.h>
int main() {
char num1=100;
int num2=(int)num1;//類型轉換格式為 (轉換後的類型)轉換的數
//比如(int)num1 num1是char類型,轉換為int類型
printf("num2=%d\n",num2);
//由於int比char取值範圍大,所以可以正常輸出結果
num2=200;
num1=(char)num2;
printf("num1=%d\n",num1);
//輸出-56 char取值範圍為(-128~127),數據溢出遵循 “模 2ⁿ” 規則(n 為位數,此處 n=8,2ⁿ=256)
//當賦值大於最大值(127)時,結果 = 賦值 - 256,代入計算:200 - 256 = -56
return 0;
}
二、分支
2.1 if 語句
#include <stdio.h>
int main() {
int age;
printf("請輸入你的年齡:");
scanf("%d",&age);
//if格式
if(age>=18)//括號裏面是條件
{
printf("你可以進網吧玩遊戲了\n");
}
2.2 if...else 語句
#include <stdio.h>
int main() {
int age;
printf("請輸入你的年齡:");
scanf("%d",&age);
//if.....else 格式
if(age>=18)//括號裏面是條件
{
printf("你可以進網吧玩遊戲了\n");//滿足條件後運行的程序
}
else//反之的意思,如果不滿足上面條件就會運行下面內容
{
printf("小朋友不準玩遊戲哦");
}
return 0;
}
if括號裏面是條件,如果滿足條件就會運行if裏面程序,如果不滿足就會運行else裏面**的程序
2.3 三目運算符
#include <stdio.h>
int main() {
int a = 10, b = 20;
int max;
//格式為 表達式1 ? 表達式2 : 表達式3;
max = (a > b) ? a : b;
printf("最大值是:%d\n", max); // 輸出:20
// 等效的if-else語句
if (a > b) {
max = a;
} else {
max = b;
}
return 0;
}
用於根據表達式1的結果(真或假)來選擇執行表達式2或表達式3,相當於簡化的if-else語句。
若表達式1的結果為 “真”(非 0 值),則整個三目表達式的值等於表達式2的值
若表達式1的結果為 “假”(0 值),則整個三目表達式的值等於表達式3的值
2.4 if…else if…else語句
● 天貓超市雙 11 推出以下優惠促銷活動:
○ 購物滿 50 元,打 9 折;
○ 購物滿 100 元,打 8 折;
○ 購物滿 200 元,打 7 折;
○ 購物滿 300 元,打 6 折;
● 編程計算當購物滿 s 元時,實際付費多少?
if (條件1) {
滿足條件1執行的代碼
} else if (條件2) { // 條件1,不滿足,才會判斷條件2
滿足條件2執行的代碼
} else if (條件3) { // 上面條件不滿足,才會判斷條件3
滿足條件3執行的代碼
} else {
上面條件都不滿足,才到else
}
1. 從上往下判斷
2. 只要有1個滿足條件,執行對應的代碼塊,後面的代碼不會再判斷執行
● 天貓超市雙 11 推出以下優惠促銷活動:
沒有打折 打折
double money, real_money;
○ 購物滿 50 元,打 9 折; money >= 50 && money < 100 real_money = 0.9 * money
○ 購物滿 100 元,打 8 折; money >= 100 && money < 200 real_money = 0.8 * money
○ 購物滿 200 元,打 7 折; money >= 200 && money < 300 real_money = 0.7 * money
○ 購物滿 300 元,打 6 折; money >= 300 real_money = 0.6 * money
else real_money = money
● 編程計算當購物滿 s 元時,實際付費多少?
○ 購物滿 300 元,打 6 折; money >= 300 real_money = 0.6 * money
○ 購物滿 200 元,打 7 折; money >= 200 real_money = 0.7 * money
○ 購物滿 100 元,打 8 折; money >= 100 real_money = 0.8 * money
○ 購物滿 50 元,打 9 折; money >= 50 real_money = 0.9 * money
else real_money = money
2.5 switch語句
- switch可以支持數據類型:int、枚舉類型、char類型
-
switch和if區別:
- 需要根據布爾條件來執行不同的代碼塊,則應使用if語句
- 需要根據表達式的值來執行不同的代碼塊,則應使用switch語句
基本格式
switch (表達式) {
case 常量1:
// 當表達式的值等於常量1時執行的語句
break; // 跳出switch結構
case 常量2:
// 當表達式的值等於常量2時執行的語句
break;
// ... 更多case分支
default:
// 當表達式的值與所有case常量都不匹配時執行的語句
break; // 可選(最後一個分支可省略)
}
-
各部分作用
-
表達式
- 必須是整數類型(int、char、enum等),不能是浮點數或字符串。其值將與各case後的常量比較。
-
case 常量
- case後必須跟常量表達式(如5、'a'、枚舉值),不能是變量。
多個case不能使用相同的常量(否則編譯報錯)。
-
break 語句
- 用於跳出switch結構,防止執行完當前case後繼續執行後續case(稱為 “穿透”)。
-
若省略break,程序會依次執行下一個case的語句,直到遇到break或switch結束。
- default 分支
- 可選分支,當表達式的值與所有case常量都不匹配時執行。
通常放在所有case之後,也可省略(此時不匹配時無任何操作)。
-
示例
#include <stdio.h>
int main() {
char grade = 'B';
switch (grade) {
case 'A':
printf("優秀\n");
break;
case 'B':
printf("良好\n"); // 執行此分支
break;
case 'C':
printf("及格\n");
break;
default:
printf("不及格\n");
}
return 0;
}
// 輸出:良好
2.6 分支綜合案例
需求:
1. 輸入:年份(整數)和月份(整數)
2. 輸出:該月份的天數(整數)
思路:
1. 定義變量保存年份、月份、天數
2. 輸入年份和月份
3. 根據月份輸出天數
- 1、3、5、7、8、10、12月 31天
- 4、6、9、11月 30天
- 2月 非閏年 28天 閏年 29天
閏年判斷:能被4整除,但不能被100整除的;或者能被400整除的年份
答案
#include <stdio.h>
int main()
{
int year, month, day;
printf("請輸入年份和月份:");
scanf("%d,%d", &year, &month);
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
day = 31;
break;
case 4:
case 6:
case 9:
case 11:
day = 30;
break;
case 2:
if ((year % 4 == 0 && year % 100 == 0) || (year % 400 != 0))
{
day = 29;
}
else
{
day = 28;
}
break;
default:
printf("輸入錯誤\n");
break;
}
printf("%d年%d月有%d天\n", year, month, day);
return 0;
}
三、循環
3.1 while語句
基本格式
while (循環條件) {
// 循環體:當條件為真時執行的語句塊
}
執行邏輯
- 判斷條件:首先檢查循環條件(一個返回布爾值的表達式,非 0 為真,0 為假)。
- 執行循環體:若條件為真,執行{}中的循環體語句;若為假,跳出循環,執行後續代碼。
- 重複過程:循環體執行完畢後,再次返回步驟 1 判斷條件,直到條件為假時結束循環。
#include <stdio.h>
int main() {
int i=0;
while(i<5)
{
printf("跑的第%d圈\n",i);
i++;
}
printf("跳出循環是第%d圈\n",i);
return 0;
}
3.2 do...while語句
do {
// 循環體:至少執行一次的語句塊
} while (循環條件); // 注意末尾有分號
執行邏輯
- 執行循環體:首先無條件執行一次{}中的循環體語句。
- 判斷條件:檢查循環條件(非 0 為真,0 為假)。
- 決定是否重複:若條件為真,返回步驟 1 再次執行循環體;若為假,跳出循環,執行後續代碼。
#include <stdio.h>
int main()
{
int i = 0;
do
{
printf("跑步第%d圈\n", i);
i++;
} while (i < 5);
printf("跳出循環第%d圈\n",i);//會至少循環一次
return 0;
}
3.3 for語句
在 C 語言中,for語句是一種功能強大的循環控制結構,特別適合循環次數已知或可預先計算的場景,其語法結構比while更緊湊。
語法格式:
for (初始化表達式; 循環條件; 更新表達式) {
// 循環體:條件為真時執行的語句塊
}
各部分作用
- 初始化表達式:循環開始前執行一次,通常用於初始化循環變量(如int i = 0)
- 循環條件:每次執行循環體前判斷,非 0 為真(繼續循環),0 為假(退出循環)
- 更新表達式:每次循環體執行後執行,通常用於修改循環變量(如i++)
初始化 → 判斷條件 → 條件為真 → 執行循環體 → 更新表達式 → 再次-------------------------------------- ↓----------------------------------------------判斷...
------------------------------------ ↓-------------------------------------------------------
------------------------------條件為假 → 退出循環-----------------------------------
基本用法(輸出 1~5)
#include <stdio.h>
int main() {
int i;
for(i=0;i<5;i++)
{
printf("跑的第%i圈\n",i);
}
printf("跳出循環是第%d圈\n",i);
return 0;
}
省略部分表達式
// 省略初始化(在外部定義變量)
int i = 0;
for (; i < 3; i++) {
printf("%d ", i); // 輸出:0 1 2
}
// 省略條件(無限循環)
for (int j = 0; ; j++) {
if (j >= 2) break;
printf("%d ", j); // 輸出:0 1
}
// 省略更新(在循環體內更新)
for (int k = 0; k < 3; ) {
printf("%d ", k);
k++; // 輸出:0 1 2
}
3.4 循環嵌套
在 C 語言中,循環嵌套指的是在一個循環(外層循環)的循環體內包含另一個循環(內層循環)。這種結構用於處理需要多層重複的邏輯,例如二維數組遍歷、矩陣操作、圖形打印等。
基本結構
// 外層循環
for (初始化1; 條件1; 更新1) {
// 內層循環(被外層循環包含)
for (初始化2; 條件2; 更新2) {
// 循環體:內層循環的執行語句
}
// 外層循環的其他語句
}
執行邏輯
- 外層循環執行一次,內層循環會完整執行所有次數
- 內層循環執行完畢後,外層循環才會進入下一次迭代
- 總執行次數 = 外層循環次數 × 內層循環次數
通過for循環嵌套打印內容: i行 j列
* * * * *
* * * * *
* * * * *
説明:
1)每行有5個*,總共有3行 3行5列
2)*之間有空格隔開
3)printf()1次只能打印1個*
答案
#include <stdio.h>
int main() {
for(int j=1;j<=3;j++){
for(int i=1;i<=5;i++){
printf("* ");
}
printf("\n");
}
return 0;
}
3.5 死循環
在 C 語言中,死循環(Infinite Loop)指的是條件始終為真、無法自行結束的循環結構。死循環本身並非總是錯誤,在某些場景下(如服務器程序、實時監控系統)是有意設計的,但多數情況下是邏輯錯誤導致的。
基本格式
// 條件恆為真(非0值均為真)
while (1) {
// 循環體
}
// 條件變量未更新,始終為真
int flag = 1;
while (flag) { // flag始終為1,循環永不結束
printf("死循環中...\n");
// 缺少flag = 0的終止條件
}
3.6 循環案例:1到100累加和
#include <stdio.h>
int main() {
int j=0;
for(int i=1;i<=100;i++){
j+=i;
}
printf("1加到100的值為:%d\n",j);
return 0;
}
四、跳轉關鍵字
- 循環和switch專屬的跳轉:break
- 循環專屬的跳轉:continue
- 無條件跳轉:goto
4.1 break
- 循環中break,某一條件滿足時,不再執行循環體中後續重複的代碼,並退出循環
- 作用:跳出當前所在的循環(for/while/do...while)或switch語句。
- 特點:僅跳出最內層的結構,不影響外層。
#include <stdio.h>
int main()
{
int i;
for (i = 1; i <= 5; i++)
{
if (i == 3)
{
printf("第%i圈太累了,不跑了\n", i);
break;
}
printf("跑的第%i圈\n", i);
}
return 0;
}
4.2 continue
- 某一條件滿足時,不再執行本次循環體中後續重複的代碼,但進入下一次循環判斷
- 作用:跳過當前循環體中剩餘的語句,直接進入下一次循環。
- 特點:僅影響當前循環的一次迭代,不終止整個循環。
#include <stdio.h>
int main()
{
int i;
for (i = 1; i <= 5; i++)
{
if (i == 3)
{
printf("第%i圈太累了,不跑了\n", i);
continue;
}
printf("跑的第%i圈\n", i);
}
return 0;
}
可以對三個進行對比
4.3 goto
- goto用於無條件跳轉,儘量少用
- 在一種情況下可使用goto語句:從一組嵌套的循環中跳出
- 作用:無條件跳轉到同一函數內的標記位置(需自定義標記)。
- 特點:可以跨越多層結構跳轉,但過度使用會導致代碼邏輯混亂(俗稱 “spaghetti code”)。
#include <stdio.h>
int main() {
int num = 0;
loop_start: // 自定義標記(格式:標記名:)
printf("請輸入一個正數:");
scanf("%d", &num);
if (num <= 0) {
printf("輸入錯誤,請重新輸入!\n");
goto loop_start; // 跳轉到標記處,重新執行
}
printf("你輸入的正數是:%d\n", num);
return 0;
}
對比總結
| 關鍵字 | 跳轉範圍 | 典型場景 |
| break | 跳出當前循環或switch | 提前終止循環、結束case分支 |
| continue | 跳過本次循環剩餘部分 | 過濾不符合條件的迭代 |
| goto | 跳轉到同一函數內的標記 | 多層循環跳出、統一錯誤處理 |
注意事項
- break和continue僅能用於循環或switch結構中。
- goto不能跨函數跳轉,也不建議在普通邏輯中頻繁使用(會降低代碼可讀性)。
- 合理使用跳轉關鍵字可以簡化邏輯,但過度依
賴會導致代碼難以維護。