C語言:位運(yùn)算、分支、循環(huán)
位运算、分支、循环
一、基础语法
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不能使用相同的常量(否则编译报错)。
- case后必须跟常量表达式(如5、‘a’、枚举值),不能是变量。
- break 语句
- 用于跳出switch结构,防止执行完当前case后继续执行后续case(称为 “穿透”)。
- 若省略break,程序会依次执行下一个case的语句,直到遇到break或switch结束。
- default 分支
- 可选分支,当表达式的值与所有case常量都不匹配时执行。
通常放在所有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不能跨函数跳转,也不建议在普通逻辑中频繁使用(会降低代码可读性)。
- 合理使用跳转关键字可以简化逻辑,但过度依
赖会导致代码难以维护。
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章