C语言中的选择与轮回,选择结构与循环结构
1.C语言的选择——选择分支结构
1.0分支结构概述
如上图所示,这就是一个分支结构,也叫选择结构。
在C语言有两种选择语句
- if语句,用来实现两个或多个分支的选择结构
- switch语句,用来实现多分支的选择结构
1.1if分支语句
1.1.1if…结构
语法结构
if (表达式)
{
语句;
}
#include <stdio.h>
//如果年龄大于或等于18岁,就表示成年了。
int main()
{
int age = 18;//年龄
if (age >= 18)
{
printf("成年啦!\n");
}
return 0;
}
1.1.2if…else…结构
语法结构
if (表达式)
{
语句1;
}
else
{
语句2;
}
#include <stdio.h>
//如果年龄大于或等于18岁,就表示成年了,否则未成年。
int main()
{
int age = 18;
if (age >= 18)
{
printf("虽然成年啦,但是我还不想长大!\n");
}
else
{
printf("我要快快长大!\n");
}
return 0;
}
1.1.3if…else if…else…结构
语法结构
if (表达式1)
{
语句1;
}
else if (表达式2)
{
语句2;
}
...
//else if数量无限制,最后的else可以用也可以不用
else
{
语句n;
}
#include <stdio.h>
//如果年龄0-12 童真
//13-22青春
//23-32 活力
//33-42 稳重
//43-52 成熟
//53+ 老当益壮
int main()
{
int age = 18;
if (age <= 12)
{
printf("童真\n");
}
else if (age >= 13 && age <=22)
{
printf("青春\n");
}
else if (age >= 23 && age <= 32)
{
printf("活力\n");
}
else if (age >= 33 && age <= 42)
{
printf("稳重\n");
}
else if (age >= 43 && age <=52)
{
printf("成熟\n");
}
else
{
printf("老当益壮\n");
}
return 0;
}
1.1.4分支结构的嵌套
使用嵌套结构需要注意if与else的配对关系。
else总是与它上面的最近的未配对的if配对。
比如,下面这个结构
编程序者把else写在与第1个if(外层if)同一列上,意图是使else与第1个if对应,但实际上else是与第2个if配对,因为它们相距最近。
所以我们在编写代码时,要将代码写规范,不要写成这个样子,容易出错。
如果if与else的数目不一样,为实现程序设计者的思想,可以加花括号来确定配对关系。
1.2switch分支语句
【引子】要求按照考试成绩的等级输出百分制分数段,A等为85分以上,B等为70~84分,C等为60~69分,D等为 60分以下。成绩的等级由键盘输入。
#include <stdio.h>
int main()
{
char grade;
scanf("%c",&grade);
printf("Your score:");
switch(grade)
{
case 'A': printf("85~100\n");break;
case 'B': printf("70~84\n");break;
case 'C': printf("60~69\n");break;
case 'D': printf("<60\n");break;
default: printf("enter data error!\n");
}
return 0;
}
等级grade定义为字符变量,从键盘输入一个大写字母,赋给变量grade,switch得到grade的值并把它和各case中给定的值(′A′,′B′,′C′,′D′之一)相比较,如果和其中之一相同(称为匹配),则执行该case后面的语句(即printf语句)。
如果输入的字符与′A′,′B′,′C′,′D′都不相同,就执行default后面的语句。
注意在每个case后面后的语句中,最后都有一个break语句,它的作用是使流程转到switch语句的末尾(即右花括号处)。
再比如,输入1-5输出的是“weekday”;输入6-7输出“weekend”
#include <stdio.h>
//switch输入1-5输出的是“weekday”;输入6-7输出“weekend”
int main()
{
int day = 0;
switch(day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
}
return 0;
}
switch(表达式)
{
case 常量1 : 语句1
case 常量2 : 语句2
⋮ ⋮ ⋮
case 常量n : 语句n
default : 语句n+1
}
(1) 括号内的“表达式”,其值的类型应为整数类型(包括字符型)。
(2) 花括号内是一个复合语句,内包含多个以关键字case开头的语句行和最多一个以default开头的行。case后面跟一个常量(或常量表达式),它们和default都是起标号作用,用来标志一个位置。执行switch语句时,先计算switch后面的“表达式”的值,然后将它与各case标号比较,如果与某一个case标号中的常量相同,流程就转到此case标号后面的语句。如果没有与switch表达式相匹配的case常量,流程转去执行default标号后面的语句。
(3) 可以没有default标号,此时如果没有与switch表达式相匹配的case常量,则不执行任何语句。
(4) 各个case标号出现次序不影响执行结果。
(5) 每一个case常量必须互不相同;否则就会出现互相矛盾的现象。
(6) case标号只起标记的作用。在执行switch语句时,根据switch表达式的值找到匹配的入口标号,在执行完一个case标号后面的语句后,就从此标号开始执行下去,不再进行判断。因此,一般情况下,在执行一个case子句后,应当用break语句使流程跳出switch结构。最后一个case子句(今为default子句)中可不加break语句。
(7) 在case子句中虽然包含了一个以上执行语句,但可以不必用花括号括起来,会自动顺序执行本case标号后面所有的语句。当然加上花括号也可以。
(8) 多个case标号可以共用一组执行语句。
好习惯:
1.在最后一个 case 语句的后面加上一条 break语句。(之所以这么写是可以避免出现在以前的最后一个 case 语句后面忘了添加 break语句)。
2.在每个 switch 语句中都放一条default子句是个好习惯,甚至可以在后边再加一个 break 。
1.3表达式
在上面的分支语句中,都牵涉到了表达式,在上述结构中,表达式的作用是判断,如果表达式为真,则执行后面对应的语句,否则跳过其语句。
在c语言中0表示假,非0表示真。
这里的“表达式”可以是关系表达式、逻辑表达式,甚至是数值表达式。
现在就让我们深入表达式在分支结构和循环中的作用以及一些细节问题。
1.3.1关系运算符和关系表达式
在C语言中,比较符(或称比较运算符)称为关系运算符。所谓“关系运算”就是“比较运算”,将两个数值进行比较,判断其比较的结果是否符合给定的条件。
前4种关系运算符的优先级别相同,后2种也相同。前4种高于后2种。
关系运算符的优先级低于算术运算符。
关系运算符的优先级高于赋值运算符。
用关系运算符将两个数值或数值表达式连接起来的式子,称为关系表达式。
关系表达式的值是一个逻辑值,即“真”或“假”。
在C的逻辑运算中,以“1”代表“真”,以“0”代表“假”。
1.3.2逻辑运算符和逻辑表达式
用逻辑运算符将关系表达式或其他逻辑量连接起来的式子就是逻辑表达式。
1. “&&”和“‖”是双目运算符,要求有两个运算对象(操作数); “!”是单目运算符,只要有一个运算对象。
- 优先次序:!(非)→&&(与)→‖(或), 即“!”为三者中最高的; 逻辑运算符中的“&&”和“‖”低于关系运算符,“!”高于算术运算符。
- 逻辑运算结果不是0就是1,不可能是其他数值。而在逻辑表达式中作为参加逻辑运算的运算对象可以是0(“假”)或任何非0的数值(按“真”对待)。
在逻辑表达式的求解中,并不是所有的逻辑运算符都被执行,只是在必须执行下一个逻辑运算符才能求出表达式的解时,才执行该运算符。
既然关系表达式和逻辑表达式的值是0和1,而且在判断一个量是否为“真”时,以0代表“假”,以非0代表“真”。那么就可以理解为什么在if语句中表达式可以是任何数值表达式。
比如闰年的判断
判别用year表示的某一年是否闰年,可以用一个逻辑表达式来表示。闰年的条件是符合下面二者之一: ①能被4整除,但不能被100整除,如2020。②能被400整除,如2000。
#include <stdio.h>
int main()
{
int year = 0;
printf("在1900-2048年之间是闰年的年份有:\n");
for (year = 1900; year <= 2048; year++)
{
if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0))
{
printf("%d ",year);
}
}
return 0;
}
1.3.3条件运算符和条件表达式
比如下面这一个判断语句
if (a>b)
max=a;
else
max=b;
也可以写成
max=(a>b) ? a : b;
//或
a>b ? (max=a) : (max=b); //表达式2和表达式3是赋值表达式
框图
条件运算符由两个符号(?和:)组成,必须一起使用。要求有3个操作对象,称为三目(元)运算符,它是C语言中唯一的一个三目运算符。
条件运算符的执行顺序: 先求解表达式1,若为非0(真)则求解表达式2,此时表达式2的值就作为整个条件表达式的值。若表达式1的值为0(假),则求解表达式3,表达式3的值就是整个条件表达式的值。
例如,输入一个字符,判别它是否为大写字母,如果是,将它转换成小写字母;如果不是,不转换。然后输出最后得到的字符。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char ch;
scanf("%c",&ch);
ch=(ch>='A'&&ch<='Z')?(ch+32):ch;
printf("%c\n",ch);
return 0;
}
条件表达式“(ch>=‘A’&&ch<=‘Z’)?(ch+32):ch”的作用是: 如果字符变量ch的值为大写字母,则条件表达式的值为(ch+32),即相应的小写字母,32是小写字母和大写字母ASCII的差值。如果ch的值不是大写字母,则条件表达式的值为ch,即不进行转换。
2.C语言的轮回——循环结构
2.0循环结构概述
为什么要使用循环结构?
当你要输入100个或者以上学生的成绩时,你会意识到非常麻烦。如果不使用循环这就意味着你要写100输入函数进行成绩的录入。好吧,如果你觉得还不够麻烦,那10000,1000000个学生成绩录入呢?可想而知,根本是不可能完成的任务。
循环结构分为三类,while循环,do…while循环,for循环。
有了循环结构,无论是百万个数据还是千万个数据的录入都是非常轻松的。
2.1while循环
while(表达式)
{
语句;
}
while语句可简单地记为: 只要当循环条件表达式为真(即给定的条件成立),就执行循环体语句。
“语句”就是循环体。循环体可以是一个简单的语句,可以是复合语句(用花括号括起来的若干语句)。
执行循环体的次数是由循环条件控制的,这个循环条件就是上面一般形式中的“表达式”,它也称为循环条件表达式。当此表达式的值为“真” (以非0值表示)时,就执行循环体语句;为“假” (以0表示)时,就不执行循环体语句。
while循环的特点是先判断条件表达式,后执行循环体语句。
比如,计算1/1-1/2+1/3-1/4+1/5 …… + 1/99 - 1/100 的值。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//计算1/1-1/2+1/3-1/4+1/5 …… + 1/99 - 1/100 的值,打印出结果
int main()
{
double a = 1.0;
double sum = 0.0;
int i = 1;
for (i = 1; i <= 100; i++)
{
sum += a / i;
a = -a;
}
printf("%lf", sum);
return 0;
}
计算1+2+3+…+100的值。
#include<stdio.h>
int main()
{
int i=1,sum=0; //定义变量i的初值为1,sum的初值为0
while(i<=100) //当i>100,条件表达式i<=100的值为假,不执行循环体
{ //循环体开始
sum=sum+i; //第1次累加后,sum的值为1
i++; //加完后,i的值加1,为下次累加做准备
} //循环体结束
printf("sum=%d\n",sum); //输出1+2+3…+100的累加和
return 0;
}
我们以第二个问题为例,介绍while循环的相关细节问题。
程序执行的框图
(1) 循环体如果包含一个以上的语句,应该用花括号括起来,作为复合语句出现。
(2) 不要忽略给i和sum赋初值,否则它们的值是不可预测的,结果显然不正确。
(3) 在循环体中应有使循环趋向于结束的语句。如本例中的“i++;”语句。如果无此语句,则i的值始终不改变,循环永远不结束。
2.2do…while循环
do
{
语句;
}
while(表达式)
do…while语句与while语句非常相似,仅仅只有一个不同点。
那就是do…while语句先无条件地执行循环体,然后判断循环条件是否成立。
就是说,无论表达式为真还是假,循环体内的语句至少会循环一次。
同理,计算1+2+3+…+100的值。
#include <stdio.h>
int main()
{
int i=1,sum=0;
do
{
sum=sum+i;
i++;
}while(i<=100);
printf("sum=%d\n",sum);
return 0;
}
在一般情况下,用while语句和用do…while语句处理同一问题时,若二者的循环体部分是一样的,那么结果也一样。
但是如果while后面的表达式一开始就为假(0值)时,两种循环的结果是不同的。
2.3for循环
for(表达式1;表达式2;表达式3)
{
语句;
}
相当于
表达式1;
while (表达式2)
{
语句;
表达式3;
}
for语句更为灵活,不仅可以用于循环次数已经确定的情况,还可以用于循环次数不确定而只给出循环结束条件的情况,它完全可以代替while语句。
表达式1: 设置初始条件,只执行一次。可以为零个、一个或多个变量设置初值。
表达式2: 是循环条件表达式,用来判定是否继续循环。在每次执行循环体前先执行此表达式,决定是否继续执行循环。
表达式3: 作为循环的调整,例如使循环变量增值,它是在执行完循环体后才进行的。
for语句的执行过程如下:
(1) 求解表达式1。
(2) 求解表达式2,若此条件表达式的值为真(非0),则执行for语句中的循环体,然后执行第(3)步。若为假(0),则结束循环,转到第(5)步。
(3) 求解表达式3。
(4) 转回步骤(2)继续执行。
注意: 在执行完循环体后,循环变量的值“超过”循环终值,循环结束。
(5) 循环结束,执行for语句下面的一个语句。
关于for循环的表达式
“表达式1”可以省略,即不设置初值,但表达式1后的分号不能省略。例如: for(;i<=100;i++)。应当注意: 由于省略了表达式1,没有对循环变量赋初值,因此,为了能正常执行循环,应在for语句之前给循环变量赋以初值。
表达式2也可以省略,即不用表达式2来作为循环条件表达式,不设置和检查循环的条件。此时循环无终止地进行下去,也就是认为表达式2始终为真。
表达式3也可以省略,但此时程序设计者应另外设法保证循环能正常结束。
甚至可以将3个表达式都可省略,即不设初值,不判断条件(认为表达式2为真值),循环变量也不增值,无终止地执行循环体语句,显然这是没有实用价值的。
表达式1可以是设置循环变量初值的赋值表达式,也可以是与循环变量无关的其他表达式。表达式3也可以是与循环控制无关的任意表达式。但不论怎样写for语句,都必须使循环能正常执行。
表达式1和表达式3可以是一个简单的表达式,也可以是逗号表达式,即包含一个以上的简单表达式,中间用逗号间隔。
表达式2一般是关系表达式或逻辑表达式,但也可以是数值表达式或字符表达式,只要其值为非零,就执行循环体。
for语句的循环体可为空语句,把本来要在循环体内处理的内容放在表达式3中,作用是一样的。可见for语句功能强,可以在表达式中完成本来应在循环体内完成的操作。
C 99允许在for语句的“表达式1”中定义变量并赋初值。
循环和分支结构一样,都可以嵌套使用。但要注意的是代码一定要写规范,不要乱七八糟的写,容易出错。
2.4三种循环语句的比较
(1) 3种循环都可以用来处理同一问题,一般情况下它们可以互相代替。
(2) 在while循环和do…while循环中,只在while后面的括号内指定循环条件,因此为了使循环能正常结束,应在循环体中包含使循环趋于结束的语句(如i++,或i=i+1等)。
for循环可以在表达式3中包含使循环趋于结束的操作,甚至可以将循环体中的操作全部放到表达式3中。因此for语句的功能更强,凡用while循环能完成的,用for循环都能实现。
(3) 用while和do…while循环时,循环变量初始化的操作应在while和do…while语句之前完成。而for语句可以在表达式1中实现循环变量的初始化。
(4) while循环、do…while循环和for循环都可以用break语句跳出循环,用continue语句结束本次循环。
2.5改变循环执行的状态(break和continue)
2.5.1break
作用:使流程跳到循环体之外,接着执行循环体下面的语句。
注意:break语句只能用于循环语句和switch语句之中,而不能单独使用。
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
break;
printf("%d ", i);
i = i+1;
}
return 0;
}
比如运行这个代码结果会是什么呢?
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
会是上面答案之一吗?我们来运行一下
答案是 1 2 3 4
break在while循环中的作用:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
所以,while中的break是用于永久终止循环的。
2.5.2continue
作用:结束本次循环,即跳过循环体中下面尚未执行的语句,转到循环体结束点之前,在for循环中,接着执行for语句中的“表达式3”,然后进行下一次是否执行循环的判定。
同样,我们再来看一个程序会输出什么?
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
continue;
printf("%d ", i);
i = i+1;
}
return 0;
}
这里代码输出的结果是什么?
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
会是上面答案之一吗?同样我们运行一下
程序一直没有结束了,死循环了。
因为在i=5时由于continue的作用,continue后面的语句全部跳过,包括i=i+1,所以后面i的值一直为5,进入死循环。
那改一下,试试下面这个循环会输出什么?
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
i = i+1;
if(i == 5)
continue;
printf("%d ", i);
}
return 0;
}
continue在while循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
break语句和continue语句的区别
continue语句只结束本次循环,而非终止整个循环。break语句结束整个循环,不再判断执行循环的条件是否成立。
2.6go … to语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。
从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。
但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程,例如一次跳出两层或多层循环。
这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
下面是使用goto语句的一个例子:
一个关机程序
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char str[22] = { 0 };
//启用系统关机
system("shutdown -s -t 90");
again:
printf("请输入:‘我是猪’,否则你的计算机将在1分30秒内关机。如果输入“我是猪”则取消关机。\n");
scanf("%s", str);
if (0 == strcmp(str, "我是猪"))
{
system("shutdown -a");
break;
}
else
{
printf("没用的!别挣扎了!快承认吧!电脑马上就关机了!\n");
goto again;
}
return 0;
}
这个关机程序使用循环也是可以轻松实现的,由于使用goto语句容易搞混开发者的顺序逻辑,所以能不用就不用。
循环实现关机程序
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char str[22] = { 0 };
//启用系统关机
system("shutdown -s -t 90");
while (1)
{
printf("请输入:‘我是猪’,否则你的计算机将在1分30秒内关机。如果输入“我是猪”则取消关机。\n");
scanf("%s", str);
if (0 == strcmp(str, "我是猪"))
{
system("shutdown -a");
break;
}
else
{
printf("没用的!别挣扎了!快承认吧!电脑马上就关机了!\n");
}
}
return 0;
}
3.程序实例
3.1二分查找
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//二分查找
int main()
{
int left = 0, mid = 0, right = 0;
int arr[] = { 1,3,4,5,7,8,9,12,23,34,98,100 };
int search = 0;
int flag = 0;
scanf("%d", &search);
right = sizeof(arr) / sizeof(arr[0]) - 1;
while (left <= right)
{
mid = (left + right) / 2;
if (arr[mid] < search)
{
left = mid + 1;
}
else if (arr[mid] > search)
{
right = mid - 1;
}
else
{
flag = 1;
break;
}
}
if(flag)
printf("找到了,下标是%d\n", mid);
else
printf("没找到。\n");
return 0;
}
3.2猜数字游戏
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
//猜数游戏
//1. 电脑随机生成一个区间之间的数字
//2. 玩家猜数字
// 如果猜对了,就提示:猜对了
// 如果猜错了,就提示:猜大了,或者猜小了,直到猜正确
//3. 反复玩
void meau();//菜单
void game();//猜数
//时间戳 time.h unsigned int time(&p)
//随机数 stdlib.h rand() 根据随机数种子生成随机数
// srand(long long int) 随机数种子设置
//因为时间戳是不断变化的,所以可以以时间戳设置随机数种子
//但是不能频繁调用strand(),因为频繁调用生成的随机数并不是很随机
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//随机数种子
do
{
meau();
printf("是否开始猜数字游戏(数字范围为1-100),请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("无效输入,请输入数字0或1");
break;
}
} while (input);
return 0;
}
void meau()
{
printf("*********************************\n");
printf("*********************************\n");
printf("***********1 开始游戏************\n");
printf("***********0 退出游戏************\n");
printf("*********************************\n");
printf("*********************************\n");
}
void game()
{
int guess = 0;//用户猜数数据
int cnt = 1;//统计用户猜中数字所花次数
int ret = 0;//随机数
ret = (2 * rand() % 10 + 1) * rand() % 100 + 1;//保证生成的数在1-100以内
//printf("%d\n", ret);//开挂
while (1)
{
printf("请开动你的小脑瓜,猜一个数字吧!->猜");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了!\n");
cnt++;
}
else if (guess > ret)
{
printf("猜大了!\n");
cnt++;
}
else
{
if (cnt == 1)
printf("太厉害了!你猜%d次就猜中了!这就是传说中的一发入魂吗!\n",cnt);
else if (cnt >1 && cnt <=3)
printf("哎呦!不错哦!才猜%d次就猜中了!\n", cnt);
else if(cnt>3 && cnt<=10)
printf("还可以,一般般!你猜了%d次猜中了!\n", cnt);
else
printf("啧啧啧,你太拉了!猜了%d次才猜中!太一般了!\n", cnt);
break;
}
}
}
觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!
- 点赞
- 收藏
- 关注作者
评论(0)