一、 分支语句
分支语句(选择结构),什么是选择?如果你好好学习,校招时拿到一个好offrer,走上人生巅峰;如果你不学习,毕业等于失业,回家卖红薯。这就是选择!!
1、if
语法结构1:
if(表达式)
语句1;
语法结构2:
if(表达式)
语句1;
else
语句2;
语法结构3(多分支):
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
这个语法结构是什么意思呢?以语法结构1为例,执行if语句前,先对括号里面表达式的结果进行判断,如果判断结果为真,执行语句1;如果判断结果为假,跳过语句1,执行后面的语句。
if和else都是默认只控制一条语句。如果条件成立,想执行多条语句,就要使用代码块。以语法结构2为例:
if(表达式)
{
语句列表1;
}
else
{
语句列表2;
}
在C语言中如何表示真和假呢?
0表示假,非0表示真
知道了如何表示真假,接下来用代码演示语法结构2和语法结构3:
int main()
{
int age = 20;
if (age >= 18)
{
printf("成年\n");//显示结果为成年
}
else
{
printf("未成年\n");
printf("不能谈恋爱\n");
}
return 0;
}
int main()
{
int age = 70;
if (age < 18)
{
printf("少年\n");
}
else if (age >= 18 && age < 26)
{
printf("青年\n");
}
else if (age >= 26 && age < 50)
{
printf("中年\n");
}
else if (age >= 50 && age < 100)
{
printf("老年\n");
}
else
printf("长寿村的秘密\n");
return 0;
}
这里思考一个代码(悬空else问题):
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
//显示结果为 不打印
为什么打印结果是不打印呢?这里就涉及到else的匹配问题。
else的匹配:else和离它最近的if匹配。
知道了else的匹配,可以知道刚才的代码中的else是和离它最近的if(第二个if)相匹配的,所以我们对刚才的代码做一点改变:
int main()
{
int a = 0;
int b = 2;
if (a == 1)
{
if (b == 2)
{
printf("hehe\n");
}
else
{
printf("haha\n");
}
}
return 0;
}
这样改写后,代码就一目了然了:执行第一个if语句时对a是否等于1判断,判断结果为假,直接跳过中间的if…else语句,所以显示结果为不打印。
2、switch
语法结构:
switch(整型表达式)
{
语句项;
}
这里的语句项是什么呢?
case 整型常量表达式:
语句;
可能这样讲有点抽象,下面进行代码演示:
switch语句的基本形式就像上面代码演示的那样。但是问题又来了,如果我现在的要求是输入2,只打印星期二怎么办?这就要用到break语句。
case决定入口,break决定出口。
接下来,我们对这个代码再进行改造:
要求:输入1-5,打印工作日;输入6-7,打印休息日。
break语句的实际效果是把语句列表划分为不同的部分。
注意:
switch语句允许嵌套使用,一个switch语句套着另一个switch语句,这时候break只会跳出自己所在的switch语句。
编程好习惯:
在最后一条case语句的后面加上一条break语句。
(之所以这么写是可以避免出现在以前的最后一个case语句后面忘了添加break语句。)
default子句
如果表达的值与所有的case标签的值都不匹配怎么办?
其实也没什么,结构就是所有的语句都被跳过而已。
程序并不会终止,也不会报错,因为这种情况在C中并不认为是个错误。
但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default子句,把下面的标签
default:
写在任何一个 case标签可以出现的位置。
当 switch表达式的值并不匹配所有 case标签的值时,这个 default子句后面的语句就会执行。所以,每个switch语句中只能出现一条default子句。
但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。
下面用代码演示default子句的使用:
编程好习惯:
在每个 switch 语句中都放一条default子句是个好习惯,甚至可以在后边再加一个 break 。
二、循环语句
1、while
语法结构:
while(表达式)
循环语句;
下面代码演示一下这个语法结构:
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d ", i);//显示结果为1 2 3 4 5 6 7 8 9 10
i++;
}
return 0;
}
在switch语句中我们见过了break语句,而while语句中也有break语句,下面我们用代码来演示一下:
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
break;
printf("%d ", i);//显示结果为1 2 3 4
i++;
}
return 0;
}
代码解释:前四次循环时if语句不执行,当i等于5时,if ( )中的表达式判断结果为真,执行break语句,跳出循环,屏幕上只打印1 2 3 4。
所以break在while语句中的作用是什么呢?
在while语句中,break用于永久的终止循环。
这里再介绍一下continue语句,介绍前还是惯例性地进行代码演示:
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
continue;
printf("%d ", i);//显示结果为1 2 3 4死循环
i++;
}
return 0;
}
代码解释:前四次循环时if语句不执行,当i等于5时,if ( )中的表达式判断结果为真,执行continue语句,跳过其后面的代码,直接去判断部分,然后进行下一次循环,由于此时i还是为5,continue语句继续执行,再次跳过其后面的代码,直接去判断部分。这个过程中i一直为5,故造成死循环。
所以continue在while语句中的作用是什么呢?
在while循环中,continue的作用是跳过本次循环中continue后面的代码,直接去while的判断部分,判断是否进行下一次循环。
这里提供一个代码引入getchar( )函数和putchar( )函数的概念:
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
return 0;
}
这个代码我们先不做解释,先介绍一下getchar( )函数和putchar( )函数的概念:
getchar()函数:
getchar() 函数用于读取用户从键盘输入的单个字符,它有一个整型的返回值,当发生读取错误的时候,返回整型值-1。当读取正确的时候,它会返回用户从键盘输入的第一个字符的ASCII码。当程序调用getchar时,运行程序时 就等着用户从按 键输入, 用户输入的字符被存放在键盘缓冲区中,直到用户按下回车键为止(回车字符(\n)也放在缓冲区中)。
当用户按下回车键之后,getchar才开始从输入流中每次读入一个字符,输入的字符不只一个的时候,后续的getchar调用不会等待用户输入,而是直接读取缓冲区中的字符,直到缓冲区中的字符读完之后,才等待用户的下一次输入,getchar函数输入数字也按字符处理。单个的getchar函数输入多于一个字符时,只接收第一个字符,也就是一个getchar函数一次只读取一个字符。
这里提供一下MSDN对getchar函数的介绍:
这里提一下介绍中对返回值(Return Value)的解释:
getchar读取失败的时候会返回EOF(相当于键盘上的CTRL+Z),这个EOF(end of file)表示的是文件结束标志。(EOF的本质就是-1)
putchar()函数:
putchar() 向终端输出一个字符。其功能是把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中,为C 库函数 ,包含在C 标准库 <stdio.h>中。其输出可以是一个字符,可以是介于0~127之间的一个十进制整型数(包含0和127),也可以是用char定义好的一个字符型变量。
当c为一个介于0~127(包括0及127)之间的十进制整型数时,它会被视为对应字符的ASCII代码,输出该ASCII代码对应的字符;当c为一个事先用char定义好的字符型变量时,输出该变量所指向的字符。当整型变量ch超出8位变量的范围时,ch则会变强制转化为8位变量(即取其低八位传过去输出),当为负数的时候,由于计算机存储负数是用补码表示的,所以传过去的二进制补码也被当做正数处理,也是取其低八位。
这里也提供一下MSDN对putchar函数的介绍:
了解getchar函数和putchar函数的概念后我们来解释一下刚才的那个代码:
我们用图来解释这个代码是怎么运行的:
代码解释:当我们从键盘中输入‘A’和按下回车键的时候,键盘缓冲区中有两个字符,一个是‘A’,另一个是转义字符‘\n’。一个getchar一次只能读入一个字符,一个putchar一次只能输出一个字符,所以按下回车键的时候,getchar读入字符‘A’,putchar输出字符‘A’,一次循环结束,下一次循环时的getchar是崭新的,而缓冲区此时还有转义字符‘\n’,会自动读入,putchar自动输出,也就是进行换行操作,所以你会发现刚刚那个图片,输出‘A’后光标自动换到下一行,然后再等你的下一次输入。当在键盘上输入CTRL+Z(上面提到了EOF等于键盘上的CTRL+Z),while( )中的表达式判断结果为假,结束循环。
这里再提供一个代码(一个关于输密码的小程序),请读者自行理解(说白了是博主理解一般,文字表达能力一般,不一定解释的好,就不误人子弟了 T o T):
int main()
{
int ch = 0;
char password[20] = { 0 };
printf("请输入密码>:");
scanf("%s", password);
//清理缓冲区
//getchar();这种写法不可取,
//这个只能清理一个字符,所以用循环清理多个字符
while ((ch = getchar()) != '\n')
{
;//死循环,直到getchar读到\n,才退出循环
}
printf("请确认密码(Y/N)>:");
ch = getchar();//这个读到回车键所以没确认
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
2、for
语法结构:
for(表达式1;表达式2;表达式3)
循环语句;
表达式1:
表达式1为初始化部分,用于初始化循环变量的。
表达式2:
表达式2为条件判断部分,用于判断循环何时终止。
表达式3:
表达式3为调整部分,用于循环条件的调整。
还是老规矩,上代码:
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("%d ", i);//显示结果为1 2 3 4 5 6 7 8 9 10
}
return 0;
}
这里我们比较一下实现同样的功能,用while语句来实现:
int main()
{
int i = 1;//初始化部分
while (i <= 10)//判断部分
{
printf("%d ", i);//显示结果为1 2 3 4 5 6 8 9 10
i++;//调整部分
}
return 0;
}
在while循环中,也存在初始化、判断、调整部分,但可能会因为实际问题的改变,这三个位置会离得很远,在查找和修改的时候不够方便。所以for的使用频率最高。
while循环中有break语句和continue语句,for循环中也有,作用和用法也是一样的。
老规矩,上代码:
for循环中的break语句:
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
{
break;
}
printf("%d ", i);//显示结果为1 2 3 4
}
return 0;
}
for循环中的continue语句:
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
{
continue;
}
printf("%d ", i);//显示结果为1 2 3 4 6 7 8 9 10
}
return 0;
}
for循环中的continue语句与while中的不太一样。while循环中的continue语句会跳过其后面的代码直接跳到判断部分,这样做是有可能跳过循环中的调整部分的;而for循环中的continue语句跳过其后面的代码,会先来到调整部分,然后再到判断部分,这样做是不会跳过调整部分的。
关于for语句的循环控制变量的建议:
1、不可在for循环体内修改循环变量,防止for循环失去控制;
2、建议for语句的循环控制变量的取值采取“前闭后开区间”的写法。
什么叫“前闭后开区间”的写法呢?形式如下:
int i = 0;
//前闭后开的写法
for(i=0; i<10; i++)
{
}
//两边都是闭区间
for(i=0; i<=9; i++)
{
}
这里提一个代码:
//请问循环要循环多少次
int man()
{
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++)
{
k++;
}
return 0;
}
这里只要你动笔算了,一定错,正确答案是循环次数为0。为什么呢?
这里for循环判断部分的表达式 k=0 这个表达式不是判断是否为0,而是把0赋值给k,表达式结果恒为0,结果为0,判断为假,不进入循环,所以循环次数为0。
3、do…while
语法结构:
do
循环语句;
while(表达式);
老规矩,上代码:
int main()
{
int i = 1;
do
{
printf("%d ", i);//显示结果为1 2 3 4 5 6 7 8 9 10
i++;
} while (i <= 10);
return 0;
}
do…while语句的特点是循环至少执行一次,但是使用的场景有限,所以不是经常使用。
do…while语句中同样也有break语句和continue语句。老规矩,上代码:
do…while循环中的break语句:
int main()
{
int i = 1;
do
{
if (i == 5)
{
break;
}
printf("%d ", i);//显示结果为1 2 3 4
i++;
} while (i <= 10);
return 0;
}
do…while循环中的continue语句:
int main()
{
int i = 1;
do
{
if (i == 5)
{
continue;
}
printf("%d ", i);//显示结果为1 2 3 4死循环
i++;
} while (i <= 10);
return 0;
}
总结:continue和break在do…while ,for ,while循环中是一样的作用,只是结构不同。
三、goto语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。
从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过 程。
例如:一次跳出两层或多层循环。
多层循环这种情况使用break是达不到目的的,它只能从最内层循环退出到上一层的循环,即一个break只能跳出一层循环。
goto语句的坏处:
1.破坏结构化,易读性差,内存中断次数陡增降低执行速度
2.goto是汇编孑遗。在C里不恰当的使用goto会破坏逻辑的完整性,增加跑飞的风险,有一个很关键的原因是因为现在编译器已经能很好的对循环进行优化,循环内部的goto语句会让优化很难控制。
所以goto语句尽量能不用就不用。