C语言的旅行者:穿越编程的迷雾——分支和循环

“愿你有前进一寸的勇气,亦有后退一尺的从容”

前言

本文详细介绍了C语言中的关系操作符、条件操作符、逻辑操作符以及分支和循环结构的使用方法。关系操作符用于比较表达式,返回真或假;条件操作符(三目操作符)根据条件选择执行不同的表达式;逻辑操作符用于构建复杂的逻辑表达式,包括逻辑取反、与、或操作符,并介绍了短路求值的概念。分支结构包括if语句和switch语句,用于根据不同条件执行不同代码块。循环结构包括for、while和do-while循环,用于重复执行代码块。此外,还介绍了break和continue语句在循环中的使用,以及goto语句的无条件跳转功能。文章强调了正确使用这些操作符和结构的重要性,以避免逻辑错误和代码维护问题。


目录

一、关系操作符

二、条件操作符

三、逻辑操作符

四、分支结构

1.if语句

2.switch语句

五、循环结构

1.for循环

2.while循环

3.do-while循环

六、break和continue

七、goto语句


一、关系操作符

用于比较的表达式,称为关系表达式,所使用的操作符为关系操作符,关系操作符需要两个操作数

  • >:大于
  • <:小于
  • >=:大于等于
  • <=:小于等于
  • ==:相等
  • !=:不相等

关系表达式通常返回1或0,表示真假

在C语言中,0为假,非0为真

注:

1.相等操作符“==”与赋值操作符“=”,因为就相差一个“=”很容易用错

变量在左,常量在右

a == 5//判断a是否等于5
a = 5//将5赋值给a

在上述代码中本来是要判断a是否等于5,要是用错了操作符,就会将5赋值给a,此时表达式恒为真,赋值表达式最后返回左边的操作数,真假取决于a的值,0为假,非0为真,本来的逻辑就会出错,而且不易发现

为避免用错后还发现不了,在判断变量变量和常量时,常量放在左边,变量放在右边,若用错了操作符,编译器就会报错,变量是无法赋值给常量的

5 == a//还是判断a是否等于5
5 = a//将a赋值给5,出错

2.多个关系操作符不能连用,比如判断a是否是在0~10范围内

0 <= a <= 10;//无法判断

只能将多个关系表达式连用来进行多条件判断,用“&&”(并且)或“||”(或者)连用,根据具体逻辑选择使用,下文有具体介绍


二、条件操作符

条件操作符也叫做三目操作符,顾名思义,需要三个操作数

exp1 ? exp2 : exp3

条件操作符的计算逻辑:

如果exp1为真,exp2计算,计算的结果是整个表达式的结果

如果exp1为假,exp3计算,计算的结果是整个表达式的结果

其中exp1、exp2、exp3都是表达式

#include <stfio.h>

int main()
{
	int a = 3;
    //用ret来接收表达式的结果
	int ret = 0 == a ? 1 : -1;//0==a不成立,为假结果为-1
	printf("%d\n", ret);

	ret = 0 != a ? 1 : -1;//0!=a成立,为真结果为1
	printf("%d\n", ret);

	return 0;
}

三、逻辑操作符

逻辑操作符提供逻辑判断功能,用于构建更复杂的表达式,主要由下面三个操作符:

1.逻辑取反操作符:!

改变单个表达式的真假,真变假,假变真

a!a
非00
01
#include <stdio.h>

int main()
{
	int a = 3;
	int ret = !(0 == a);//0==a为假,!(0==a)为真,非0值
	printf("%d\n", ret);

	ret = !(0 != a);//a!=0为真,!(a!=0)为假,0
	printf("%d\n", ret);

	return 0;
}

2.与操作符:&&

与操作符,并且的意思,是一个双目操作符,使用方式是a&&b,&&两边的表达式都是真的时候整个表达式才为真,只要有一个是假,则整个表达式为假,一假则假

aba&&b
非0非01
非000
0非00
000
#include <stdio.h>

int main()
{
	int a = 3;
	int ret = a > 0 && a < 5;//都为真则真
	printf("%d\n", ret);//1

	ret = a > 0 && a < 2;//一假则假
	printf("%d\n", ret);//0

	ret = a < 2 && 0 == a % 2;//都为假则假
	printf("%d\n", ret);//0

	return 0;
}

3.或操作符:||

或操作符,或者的意思,是一个双目操作符,使用方式是a||b,||两边的表达式只要有一个是真则整个表达式为真,两个都为假,则整个表达式才为假,一真则真

aba||b
非0非01
非001
0非01
000
#include <stdio.h>

int main()
{
	int a = 3;
	int ret = a > 0 || a < 5;//都为真则真
	printf("%d\n", ret);//1

	ret = a > 0 || a < 2;//一真则真
	printf("%d\n", ret);//1

	ret = a < 2 || 0 == a % 2;//都为假则假
	printf("%d\n", ret);//0

	return 0;
}

4.短路

C语言逻辑运算符还有一个特点,它总是先对左边的表达式求值,再对右边的表达式求值,这个顺序是保证的。 如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值,这种情况称为“短路”。

对于&&操作符来说,左边的操作数为0时,右边的操作数就不再执行

对于||操作符来说,左边的操作数为1时,右边的操作数就不再执行

仅仅根据左边操作数的结果就能知道整个表达式的结果,不再对右边的操作数进行计算的运算称为短路求值

#include <stdio.h>

int main()
{
	int flag = 0;
	int b = 0;

	flag && (b = 9);//flag为0,&&不会再进行右边的b=9
	printf("%d\n", b);//结果为0

	flag = 1;
	flag || (b = 9);//flag为1,&&不会再进行右边的b=9
	printf("%d\n", b);//结果为0

	return 0;
}

四、分支结构

C语言的分支结构通过条件判断控制程序执行路径,主要分为if语句和switch语句两类,用于在不同条件下选择不同的代码块执行

1.if语句

(1)if

单分支机构,语法形式:

if(exp)
    statement;//具体语句

表达式exp成立(为真),则statement执行,表达式不成立(为假),则不执行

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d", &a);
    //判断是否为偶数
    if(a % 2 == 0)
        printf("偶数\n");

    return 0;
}

(2)else

双分支结构,使用if-else处理两种可能性,语法形式:

if(exp)
    statement1;
else
    statement2;

表达式exp成立(为真),则statement1执行,表达式不成立(为假),则statement2执行

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d", &a);
    //判断奇偶数
    if(a % 2 == 0)
        printf("偶数\n");
    else
        printf("奇数\n");

    return 0;
}

(3)分支中包含多条语句

如果在一次条件判断下要执行多条语句

#include <stdio.h>

int main()
{
    int score = 0;
    scanf("%d", &score);
    //判断成绩是否及格,若及格显示“及格了”和“太棒了!”
    if(score >= 60)
        printf("及格了\n");
        printf("太棒了!\n");

    return 0;
}

若直接按上述写,无论score是否>=60,第二条printf语句都会执行

因为if/else默认都只能控制一条语句,若要控制多条语句(代码块),将代码块放在{}内

#include <stdio.h>

int main()
{
    int score = 0;
    scanf("%d", &score);
    //判断成绩score是否及格,若及格显示“及格了”和“太棒了!”
    if(score >= 60)
    {
        printf("及格了\n");
        printf("太棒了!\n");
    }

    return 0;
}

(4)嵌套if

在if-else语句中,else语句中,else可以与另一个if语句连用,构成多重判断

#include <stdio.h>

int main()
{
	int score = 0;
	scanf("%d", &score);
	//判断成绩等级
	if (score >= 90)
		printf("A\n");
	else if (score >= 80)
		printf("B\n");
	else
		printf("C\n");

	return 0;
}

也可层层嵌套

#include <stdio.h>

int main()
{
	int score = 0;
	scanf("%d", &score);
	//判断成绩是否及格
	if (score < 60)
		printf("不及格\n");
	else
	{
		printf("及格\n");
		//及格判断成绩等级
		if (score >= 90)
			printf("A\n");
		else if (score >= 80)
			printf("B\n");
		else
			printf("C\n");
	}

	return 0;
}

(5)悬空else问题

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	if (a == 0)
		if (b == 2)
			printf("hello!\n");
	else
		printf("byebye!\n");

	return 0;
}

上述代码的预期逻辑:希望当 a != 0 时执行else语句输出"byebye!"

但代码实际运行起来无任何输出,这就是else悬空问题

以为else是和对齐的if(a == 0)匹配的,其实并不是,因为C语言规定else总是与最近未匹配的if配对,此处else是和if(b == 2)所匹配

上述代码写成以下形式更好理解

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	if (a == 0)
	{
		if (b == 2)
			printf("hello!\n");
		else
			printf("byebye!\n");
	}

	return 0;
}

当外层if(a == 0)不满足时便会跳过内层的if-else,所以没有输出

实现原来的逻辑的解决方案:使用大括号{}明确作用域

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	if (a == 0)
	{
		if (b == 2)
			printf("hello!\n");
	}
	else
		printf("byebye!\n");

	return 0;
}

通过这个例子可以清晰看出,未正确使用代码块括号会导致逻辑与预期不符

建议在编写嵌套条件语句时,始终用{}明确作用域,避免此类问题

(6)判断闰年

判断年份year是否为闰年

判断闰年的规则:

                            ①能被4整除且不能被100整除的是闰年

                            ②能被400整除的是闰年

#include <stdio.h>

int main()
{
	int year = 0;
	printf("请输入年份:");
	scanf("%d", &year);
	if (year % 4 == 0 && year % 100 != 0)
		printf("是闰年\n");
	else if (year % 400 == 0)
		printf("是闰年\n");
	else
		printf("不是闰年\n");

	return 0;
}

可以把是闰年的部分放在一起判断,(能被4整除且不能被100整除)或(能被400整除) 

#include <stdio.h>

int main()
{
	int year = 0;
	printf("请输入年份:");
	scanf("%d", &year);
	if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
		printf("是闰年\n");
	else
		printf("不是闰年\n");

	return 0;
}

2.switch语句

switch是一种多分支结构,是一种特殊形式的if-else结构,用于判断有多个结果的情况

switch(exp)
{
    case value1:
        statement1;
        break;
    case value2:
        statement2;
        break;
    //...
    default:
        statement;
        break;
}        

根据表达式exp不同的值,执行相应的case分支,如果找不到对应的case分支,就执行default分支,default子句处理未匹配的情况,通常置于末尾但非必须,case和default无顺序要求,视情况而定,满足情况即可 

注:switch后的exp必须是整型表达式,case后的value必须是整型表达式,case和后边的value必须有空格

判断月份为哪一季度:

#include <stdio.h>

int main()
{
	int month = 0;
	printf("请输入月份:");
	scanf("%d", &month);
	switch (month)
	{
	case 1:
		printf("第一季度\n");
		break;
	case 2:
		printf("第一季度\n");
		break;
	case 3:
		printf("第一季度\n");
		break;
	case 4:
		printf("第二季度\n");
		break;
	case 5:
		printf("第二季度\n");
		break;
	case 6:
		printf("第二季度\n");
		break;
	//此处省略三四季度
	default:
		printf("输入错误\n");
		break;
	}

	return 0;
}

若将上述代码中的break去掉,此时会发现,不仅会执行对应case分支,还会执行后续所有的分支

#include <stdio.h>

int main()
{
	int month = 0;
	printf("请输入月份:");
	scanf("%d", &month);
	switch (month)
	{
	case 1:
		printf("第一季度\n");
	case 2:
		printf("第一季度\n");
	case 3:
		printf("第一季度\n");
	case 4:
		printf("第二季度\n");
	case 5:
		printf("第二季度\n");
	case 6:
		printf("第二季度\n");
	//此处省略三四季度
	default:
		printf("输入错误\n");
	}

	return 0;
}

break用于防止case穿透,若无则会顺序执行后续代码块,case是决定入口,若不加break,则会进入下一个case,每一个case语句中的代码块执行完后,需要加上break,才能跳出swtich语句,加不加break视情况而定

因此,利用这个特点可以将代码改写的更简洁

#include <stdio.h>

int main()
{
	int month = 0;
	printf("请输入月份:");
	scanf("%d", &month);
	switch (month)
	{
	case 1:
	case 2:
	case 3:
		printf("第一季度\n");
		break;
	case 4:
	case 5:
	case 6:
		printf("第二季度\n");
		break;
	//此处省略三四季度
	default:
		printf("输入错误\n");
		break;
	}

	return 0;
}

五、循环结构

循环结构用于重复执行某段代码,C语言支持for、while、do-while三种形式,核心在于循环变量初始化条件判断变量更新

1.for循环

for循环语法结构:

for(初始化; 条件; 更新)
    循环语句;

初始化:循环变量的初始化,只执行一次(所以可以在此处创建循环变量)

条件:判断循环结束的判断

更新:循环变量的调整

和if一样,若要控制多条语句(代码块),要加上{}

特点:初始化、条件、更新可省略,但分号必须保留,例如for(;;)表示无限循环

计算1~100之和:

#include <stdio.h>

int main()
{
	int sum = 0;
	for (int i = 1; i <= 100; i++)
	{
		sum += i;
	}
	printf("sum = %d\n", sum);

	return 0;
}

2.while循环

while循环语法结构:

while(表达式)
    循环语句;

表达式为真,执行循环语句,再进行判断,若为真则再一次执行,直至表达式为假,终止循环

若控制多条语句需放在{}内

特点:先判断条件再执行循环体,可能一次都不执行

例如读取输入直到用户输入0:

#include <stdio.h>

int main()
{
	int num;
	while (scanf("%d", &num) && num != 0) 
	{
		// 处理num
	}

	return 0;
}

 while循环中也有对应for循环中的3个部分:初始化、条件、更新,只是比较分散,不利于代码的维护,for循环将这三部分集中在一起,解决了这一问题

#include <stdio.h>

//计算1~100之和
int main()
{
	int sum = 0;
	int i = 1;//初始化
	while (i <= 100)//条件
	{
		sum += i;
		i++;//更新
	}
	printf("sum = %d\n", sum);

	return 0;
}

3.do-while循环

do-while循环语法结构:

do
    循环语句;
while(表达式);

先执行循环语句,再进行判断,若为真则再一次执行,直至表达式为假,终止循环

若控制多条语句需放在{}内

注:最后的while()后有一个分号,很多人容易忘记导致出现语法错误

特点:至少执行一次循环体,适合需要先执行再判断的场景

例如输入验证:

#include <stdio.h>

int main()
{
	int input = 0;
	do {
		printf("请输入1-10的数字:");
		scanf("%d", &input);
	} while (input < 1 || input > 10);

	return 0;
}

六、break和continue

break和continue是循环控制语句

break:永久的终止循环,break语句执行,就会直接跳出循环,继续往后执行

#include <stdio.h>

int main() 
{
    for (int i = 0; i < 10; i++) 
    {
        if (i == 5) 
            break;  // i=5时终止循环
        printf("%d ", i);//打印0 1 2 3 4
    }
    return 0;
}

break仅作用于最近的封闭结构,在嵌套场景中:

循环中嵌套的switch语句,switch中的break只能跳出switch,不能跳出外层的循环

双层循环嵌套,内层循环的break只能跳出内层循环,不会跳出外层的循环

continue:跳过本次循环continue后边的代码

例如for循环中跳过偶数:

#include <stdio.h>

int main()
{
    for (int i = 1; i <= 10; i++) 
    {
        if (i % 2 == 0) 
            continue;
        printf("%d ", i); // 输出1 3 5 7 9
    }

    return 0;
}

在for循环和while循环中有所差异,在for循环中continue不会影响到循环变量的更新,在while循环中循环变量的更新在循环体内部,所以continue的位置会影响到循环变量的更新,如果循环变量的更新在continue之后可能造成死循环

#include <stdio.h>

int main() 
{
    int i = 1;
    while (i <= 10) 
    {
        if (i == 5)
            continue;//触发后跳过后续代码
        printf("%d ", i);//打印1 2 3 4
        i++;//被跳过的代码
    }

    return 0;
}

上述代码会陷入死循环,因为当i==5时,continue跳过i++,此时i永远保持为5,循环条件i<=10始终成立,导致无限循环

避免死循环:

  • 调整变量位置:将循环变量的更新放在continue之前
  • 使用for循环替代:当需要频繁跳过循环体时,for循环更安全
  • 添加冗余条件:在循环体中额外检查终止条件

使用for循环代替:

#include <stdio.h>

int main()
{
	for (int i = 1; i <= 10; i++) 
	{
		if (i == 5) 
			continue;
		printf("%d ", i);
	}

	return 0;
}

七、goto语句

goto是C语言中的无条件跳转语句,可以实现在同一个函数内通过标签(Label)将程序执行流转移到指定位置 

goto的语法结构:

goto label;  // 跳转到标签
...
label:       // 标签定义(以冒号结尾)
    目标代码;

例如可以直接跳转:

#include <stdio.h>

int main() 
{
    printf("开始执行\n");
    goto skip;    // 跳转到标签 skip
    printf("这段代码不会执行\n");
skip:
    printf("跳转到这里\n");

    return 0;
}

跳出多层循环:

#include <stdio.h>

int main() 
{
    for (int i = 0; i < 3; i++) 
    {
        for (int j = 0; j < 3; j++) 
        {
            if (i == 1 && j == 1)
                goto exit;  // 直接跳出所有循环
            printf("i=%d, j=%d\n", i, j);
        }
    }
exit:
    printf("循环已终止\n");

    return 0;
}

注:goto容易出现bug,尽量避免使用

       在多层循环的代码中,如果想快速跳出循环使用goto就非常方便

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值