C语言学习笔记07-1-语句结构:选择/循环(附三元运算符;DevC可以for定义临时变量的设置;continue、break、return;goto语句简单说明)

选择/分支 与 循环/重复 语句结构

本质与流程图的三种结构类似,只是C语言没有until型循环,不难理解。文末捎带说一下goto语句。

结构化语句可以实现程序单一出口,goto 是非结构化的,会打乱程序的运行结构,建议是初学者慎用goto。

1. 选择/分支语句(if语句和switch语句语法)

1.1. C语言if语句形式(附三目运算符)

(1) if (条件) 表达式;  (表达式可以换行缩进写;下面条件用ccc代表,表达式用字母代替。)
   或者:
  if (ccc) {
    xxx1;
    xxx2;
  }
(2) if (ccc) xxx;
     else yyy;
   或者:
   if (ccc) {xxx1; xxx2;}  else {yyy1; yyy2;}
(3)级联形式(与多个if的连用效果不同,它的本质是多个if else 的嵌套):
if (ccc1) {… …}  else if (ccc2) {… …}  else if (ccc3) {… …}  else {… …}
(一个基础的判断原则:用大于号判断时,数值从高处往下走;用小判断从低开始走。)

顺带一提三元运算符,形如:<表达式1> ? <表达式2> : <表达式3>,表达式1通常是逻辑/关系表达式,表达式2和表达式3可以是常量、变量或表达式,含义是:如果表达式1为真则执行表达式2,否则执行表达式3,并返回执行结果。优先级仅高于赋值运算符,?:遵循就近匹配原则,编程时注意是否需要加括号。

注意点:
(i)条件的表示是逻辑表达式,主要运用关系运算符,条件只有真(值为1,或值非0)或假(值为0)两种。注意编程中==与=的差异
(ii)if-else语句可以没有{},但此时只有紧跟的一句语句受其控制。
(iii)else总和最近的还没有与else匹配的if匹配,if elseif else也是一样。(勤加{}减少出错)
(iv)if语句支持嵌套,三元运算符也能嵌套

比较两数的大小——

方案1:

在这里插入图片描述

方案2:

在这里插入图片描述

方案3:

在这里插入图片描述

比较三个数大小——

方案1:

在这里插入图片描述

方案2:

在这里插入图片描述
或者写成:
在这里插入图片描述
此时务必加第一组(),否则会出错。

1.2. switch-case(切换-情况)

switch (变量表达式) {
  case 常量1: 语句;break;
  case 常量2: 语句;break;
  case 常量3: 语句;break;
  default: 语句;break;
}

switch语句作用是提高一些情况下级联的if-else语句的执行效率,可以嵌套。
注意事项:
(1)switch()的参数类型只能是整数型结果;
(2)case标签必须是常量表达式(整数型的常数或常数计算表达式,C99支持此处是整数型常变量),case标签必须唯一(不同case不能出现相同的值);
(3)当变量表达式的计算的值与一个case的常量相等时,就从上往下依次执行后面所有的case中的语句直到switch语句结束,除非使用break;语句跳出switch语句,如果没有符合的常量就执行default中的默认语句。
(4)switch中不能单独用continue循环控制,除非是写在循环中的switch。(if类似)

判断成绩——

#include <stdio.h>

int main() {
	printf("输入成绩0~100:");
	double grade = 0;
	scanf("%lf", &grade);
	while (getchar() != '\n') {;}
	
	switch ((int)grade / 10) { //强制类型优先级仅次于最顶级优先级
		case 10:
//			{
//				if (grade > 100) break;
//			}
		case 9:
			{
				printf("A.\n");
			}
			break;
		case 8:
			{
				printf("B.\n");
			}
			break;
		case 7:
			{
				printf("C.\n");
			}
			break;
		case 6:
			{
				printf("D.\n");
			}
			break;
		case 5:
		case 4:
		case 3:
		case 2:
		case 1:
		case 0:
			{
				printf("F.\n");
			}
			break;
		default:
			{
				printf("Error!\n");
			}
			break;
	}
	
	char rank;
	printf("请输入等级(如:A):");
	scanf("%c", &rank);
	
	switch (rank) {
		case 'A': printf("90~100\n"); break;
		case 'B': printf("80~89\n"); break;
		case 'C': printf("70~79\n"); break;
		case 'D': printf("60~69\n"); break;
		case 'F': printf("0~59\n"); break;
	}
	
	return 0;
}

一年的第几天?(闰年判断)——
闰年:公历年份是4的倍数且不是100的倍数是闰年 或者 公历年是整百数的必须是400的倍数也是闰年。闰年366天(2月29天),平年365天。

	int year, month, date,
		isLeap, //闰年 leap year
		days = 0; //天数
		
	printf("输入日期(格式:yyyy-mm-dd),"
		"如2023-1-11,请输入:");
	scanf("%d-%d-%d", &year, &month, &date);
	isLeap = 
		(year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
	switch (month) {
		case 12: days += 30;
		case 11: days += 31;
		case 10: days += 30;
		case 9: days += 31;
		case 8: days += 31;
		case 7: days += 30;
		case 6: days += 31;
		case 5: days += 30; 
		case 4: days += 31;
		case 3: days += 28 + isLeap;
		case 2: days += 31; //1月天数,注意是错开的
		case 1: days += date; break;
		default: printf("month error"); return -1;
	}
	
	printf("%d-%02d-%02d是这一年的第%d天。\n", 
		year, month, date, days);

2. 循环/重复语句(3类)——“何时是个头?”

循环的逻辑一定要想明白,要符合需求,逻辑错误是没有报错的。次数固定选择for,必须先做选择dowhile,其他情况选while。循环也支持嵌套。

2.1. while(当型,先判断后做) in loop~~~

if与while的区别就是执行一次还是多次。关键是循环内要有改变条件的机会,否则会陷入死循环(除非是你想要一个永远执行的循环,一般需要避免死循环)。
注意语法正确和逻辑正确的不同;检查方式:debug调试,printf输出代码位置here1或变量值、地址等,边界值覆盖测试。重点是会思考循环计算中变量值的变化及输出的情况,思考循环次数(初值0、1或n与条件<还是<=等)
循环计算位数——
初始版是用if + /10,无法计算任意位
循环处理本质是数数,但是计算机不能预先看到位数,因此只能倒着从个位数起。
在这里插入图片描述

2.2. do…while(先做再判断)

在这里插入图片描述

两种while循环与until循环(C没有until)的差异,while是满足这个条件下执行,until是到满足这个条件时停止(不满足时执行)。
dowhile注意while()后一定加; 不要忘记!!!

来个练习:整数求逆——
思路:对一个整数%10得到个位数,/10就去掉了个位数。循环这样的操作就实现了对整数的分解。不同输出要求不同处理。
在这里插入图片描述
如果要正序输出呢?最简单的想法:两次逆序负负得正,但无法处理结尾有0的情况,解决办法先确定位数或一个用来/出最高位的数,再做其它处理。

整数正序分解——

#include <stdio.h>

int main() {
	int x = 13425, cnt = 0; //7000 0
	scanf("%d", &x);
	
	int mask = 1, tmp = x;
	//位数判断
	while (tmp > 9) {
		tmp /= 10;
		cnt ++;
		mask *= 10; //double  pow    10 cnt-1
	}
	//printf("%d",mask);
	do {
		int d = x / mask;
		printf("%d", d);
		if (mask > 9) {
			printf(" ");
		}
		x %= mask;
		mask /= 10;
		//printf("x=%d,mask=%d,d=%d\n",x,mask,d);
	} while (mask > 0);
	printf("\n");
	
	
	
	//逆序再逆序
//	int ret = 0;
//	do {
//		int d = x % 10;
//		ret = ret * 10 + d;
//		x /= 10;
//	} while (x > 0);
//	printf("ret = %d\n", ret);
//	
//	x = ret;
//	do {
//		int d = x % 10;
//		printf("%d", d);
//		if (x >= 10) {
//			printf(" ");
//		}
//		x /= 10;
//	} while (x > 0);
//	printf("\n");
	
	return 0;
}

补充:1. 求平均数套路,初值0累加求和,同时循环计数。2. 求阶乘n!:求积的小套路是初值为1。

2.3. for循环——“最古老的循环”

for(初始条件; 循环条件; 每轮循环的必要动作) {;;…;} 。for也是先判断再做,三个表达式可以选择省略,具体看需求及解决方式是如何的。当初值和动作为空时 for( ;条件; ) == while(条件)

for循环括号中定义临时变量的方式需C99支持,有时C编译器还需要添加额外选项,C++是直接可以这样写的。
如Dev中,工具-编译选项-编译器,勾选编译时加入以下命令,添加命令选项 -std=c99
在这里插入图片描述
同时选择代码生成/优化->C编译器:->支持所有ANSI C标准为No
在这里插入图片描述
这样devcpp就支持定义循环临时变量了:
99乘法表1:
在这里插入图片描述
在这里插入图片描述
99乘法表2:
在这里插入图片描述
在这里插入图片描述

输出时先j后i是因为i控制了列。

素数判断——
素数(质数):在大于1的自然数中,只能被1和自己整除的数是素数,反之是合数。

此处素数与上面的闰年的判断中,包含一个小套路:设置标志的变量如isPrime或isLeap(exit、flag),其初值1或0及计算结果1或0,可辅助做判断或计算。
在这里插入图片描述
【修改一下判断条件:】在这里插入图片描述
【可以避免负数、0、1的误判。】
当判断出不是素数时break直接跳出循环,不用再跑完整个循环了。

简单素数表——

	int cnt = 0;
	int x = 100;
	
	printf("打印素数表,如您想知道100以内的素数就输入100,:"); 
	scanf("%d", &x);
	
	for (int i = 2; i <= x; i++) {
		int isPrime = 1;
		for (int j = 2; j < i; j++) {
			if (i % j == 0) {
				isPrime = 0;
				break;
			}
		}
		if (isPrime == 1) {
			cnt ++;
			printf("\t%d", i);
			if (cnt % 5 == 0) {
				printf("\n");
			}
		}
	}
	printf("\n共有%d个素数。\n", cnt);

确定范围内的素数统计并求和——

	int m, n;
	scanf("%d %d", &m, &n);
	
	if (m == 1) {
		m = 2;
	}
	int cnt = 0, sum = 0;
	for (int i = m; i <= n; i ++) {
		int isPrime = 1;
		for (int j = 2; j < i; j ++) {
			if (i % j == 0) {
				isPrime = 0;
				break;
			}
		}
		if (isPrime) {
			cnt ++;
			sum += i;
		}
	}
	printf("%d %d", cnt, sum);
2.4. 关于continue、break 和 return

循环控制语句,不支持在分支语句中单独用,除了switch中的break。
continue:跳过本次循环后面的执行语句,进入本个循环的下一轮(你别干了,下一个)
break:跳出一个循环,到循环外了(都别干了)。
(跳出多重循环的方法:循环结尾 if(exit == 1) break; 接力break。除此外还可以使用goto语句)

return是表示从被调函数返回主调用处继续执行,可以附带一个返回值,用来结束函数。返回值类型必须与所在函数类型一致,如在void函数中只可写return ; 而在int型函数中return必须跟上一个整数型返回值。int型返回0表示正常退出,错误时可以是1、-1等表示异常终止;指针型返回相应的地址,错误时返回NULL。返回位置是调用处的下一条语句处。
【main函数返回一个浮点数会取整、返回字符型value值ascii码、不能不写返回值。】

3. goto语句介绍

可以直接进行代码间的跳转,接近于汇编语言,拥有极高的效率(比switch效率更高)。
使用方式:在代码中某处预先写一个锚点(标记label或其它易识别的符号)如 label: 表达式; ,在需要跳转到此处时的另一个位置处写 “goto label;” 即可。goto和标签必须处于同一个函数内,是函数内无条件跳转而不可跨函数。标签的作用域在函数内部,可以在goto之前或之后,不遵循先定义再使用。

参考 mooc翁恺 和 B站up考鼎录的讲解:
经常使用的场景:
(1)跳出多重循环。
(2)异常处理:函数出错时goto fail进行统一释放分配的内存( if 判断需要释放哪些)。一般C程序中常见goto failed、goto error、goto done。

与goto语句异曲同工的用法:
do {
  if(ccc) break;
  …
} while (0);
但是存在争议是如果这个循环体很长可读性下降,另外它增加了缩进级数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值