C语言是结构化的程序设计语言
顺序结构
选择结构 if switch
循环结构 for while do while
语法结构:
if(表达式)
语句 ;
if(表达式) 语句1;
else
语句2;
//多分支
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
注意:多分支语句格式:if else if else if… else
若if、else后面要跟多条语句,需要加大括号{},一对大括号就是一个代码块
c语言的真假:0表示假,非0表示真 (或者用布尔类型)
试想,下面一段代码的输出结果是什么呢
#include
int main()
{
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
当我们看一眼这个代码时,觉得输出的是haha,其实不是这样的,因为C语言中对齐与否不代表配对关系,即第一个if和else对齐但不配对,与else配对的是最接近else的一个if,所以这段代码稍作修改如下:
#include
int main()
{
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
那么这里很明显,a=0不满足第一个if的条件,程序运行根本不进入if语句中,因此这段代码什么都不输出
到这里,我们试着写一个简单的程序:输出1-100之间的奇数
#include
int main()
{
int i=1;
while(i<=100)
{
if(i%2==1)
printf("%d",i);
i++;
}
return 0;
}
switch语句也是一种分支语句,常常运用到多分支的情况
下面是根据输入1-7显示星期的程序
#include
int main()
{
if (1 == day)
printf("星期1\n");
else if (2 == day)
printf("星期2\n");
else if (3 == day)
printf("星期3\n");
else if (4 == day)
printf("星期4\n");
else if (5 == day)
printf("星期5\n");
else if (6 == day)
printf("星期6\n");
else if (7 == day)
printf("星期天\n");
return 0;
}
其实这段代码还有另一种表达方式
#include
int main(){
int day=0;
scanf("%d",&day);
switch(day)
{
case 1: //case 后必须是整型常量表达式
printf("星期1\n");
break;
case 2:
printf("星期2\n");
break;
case 3:
printf("星期3\n");
break;
case 4:
printf("星期4\n");
break;
case 5:
printf("星期5\n");
break;
case 6:
printf("星期6\n");
break;
case 7:
printf("星期天\n");
break;
}
return 0;
}
对switch语句的理解:case是各种情况的入口,当执行完后跳出(break)switch语句,break的作用即为分支
而switch后的值,必须是整型常量表达式
现在我们对这个程序要求稍作修改:当输入1-5时打印工作日,当6-7时打印休息日
对上述程序简单修改如下:
#include
int main(){
int day=0;
scanf("%d",&day);
switch(day)
{
case 1: //case 后必须是整型常量表达式
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;
case 7:
printf("休息日\n");
break;
}
return 0;
}
不难发现,情况(case)1-5都对应相同的结果,而情况(case)6-7对应一种结果,所以面对上述冗余的代码,我们可以尝试对其进行改进,结果如下:
#include
int main(){
int day=0;
scanf("%d",&day);
switch (day)//整型表达式
{
case 1: //case 后必须是整型常量表达式
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("输入错误\n");
break;
}
}
因此,case后的break需不需要加,取决于你的逻辑目的。为了解决当输入的数字不是1-7时,我们使用default来解决不匹配的问题。
书写时,不要忘记case后加冒号
另外,switch语句允许嵌套使用,例:
#include
int main()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:m++;
case 2:n++;
case 3:switch (n) //switch允许嵌套使用
{
case 1:
n++;
case 2:
m++;
n++;
break;
}
case 4:m++;
break;
default:
break;
}
printf("m = %d, n = %d\n" , m, n);
return 0;
}
关于contine:continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
而break的作用是直接跳过后面的所有循环
几个例子
#include
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
//这里的代码适当的修改是可以用来清理缓冲区的
getchar 首先,getchar是一个函数,int getchar(void),它的返回值为ASCII码,所有ASCII码表里有的字符它都能读取出来,因此getchar这个函数的返回类型是int型,而当读取错误时(ch = getchar()) == EOF),EOF返回值为-1。
我们来分析下面这个程序:
#include
int main()
{
char password[20]={0}; //初始化数组
printf("请输入密码:");
scanf("%s",password); //password是数组名不用取地址
printf("请确认密码(Y/N):");
int ch=getchar(); //ch存的是用户输入的字符的ASCII码
if(ch=='Y'){
printf("确认成功");
}
else {
printf("确认失败");
}
return 0;
}
程序运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ulAG2I9-1662121651971)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830170932774.png)]
我们会发现,当输入密码之后,还没有输入Y/N,就打印了确认失败
问题在于我们用scanf读取数据时,并不是键盘输入即被scanf给读取,而是暂存到输入缓冲区中:
scanf——————————> abcccc\n 键盘
|
输入缓冲区(\n代表回车)
现在scanf检测到输入缓冲区里有内容,就将它们读取走 ,而读取的是abcccc,放入password中储存
此时输入缓冲区只剩 \n
getchar读取时也遵循此原理,此时getchar读取到缓冲区有\n,储存到ch中,并继续判断是否为’Y’
改进方法:提前将输入缓冲区清理:
#include
int main()
{
char password[20]={0}; //初始化数组
printf("请输入密码:");
scanf("%s",password); //password是数组名不用取地址
//清理\n
getchar(); //用getchar()函数将\n读取后不返回
printf("请确认密码(Y/N):");
int ch=getchar(); //ch存的是用户输入的字符的ASCII码
if(ch=='Y'){
printf("确认成功");
}
else {
printf("确认失败");
}
return 0;
}
程序执行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-meLqw0Zw-1662121651972)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830172312503.png)]
那么现在,是否已经解决问题了呢?我们换一种输入情况验证,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWba6cnQ-1662121651972)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830172425114.png)]
这里我们在输入时加入了空格,程序运行仍然有问题
这里的问题在于,scanf遇到空格时会停止读取,因此这是输入缓冲区还剩cccc\n,而getchar()没有将输入缓冲区全部清理干净,因此出现了上述情况,这时我们不妨设计一个能完全清理掉输入缓冲区的程序:
#include
int main()
{
char password[20]={0}; //初始化数组
printf("请输入密码:");
scanf("%s",password); //password是数组名不用取地址
//完全清理输入缓冲区
int tmp=0;
while((tmp=getchar())!='\n')
{
; //一直读取字符直到读取到\n(清除)
}
printf("请确认密码(Y/N):");
int ch=getchar(); //ch存的是用户输入的字符的ASCII码
if(ch=='Y'){
printf("确认成功");
}
else {
printf("确认失败");
}
return 0;
}
此时程序执行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NR0GkPGs-1662121651973)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830173332190.png)]
下面我们来看另一个例子:
#include
int main()
{
int ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
return 0;
}
该段代码的作用是:只打印数字字符,跳过其他字符
for(表达式1;表达式2;表达式3){
循环体;
}
例:
for(i=1;i<=10;i++){
printf("%d",i);
}
表达式1是初始化部分,表达式2是判断部分,表达式3是调整部分,用于循环条件的调整。
执行的顺序是:表达式1------>表达式2------>循环体------>表达式3
那么for循环同样的有break和continue的用法,与while循环的区别是,for循环接continue不会像while循环一样可能会出现死循环,因为continue跳过后面的循环体,并不会跳过“i++”(调整部分)
综上,for循环比while循环更好用
一些建议:不可在for 循环体内修改循环变量,防止 for 循环失去控制;for语句的循环控制变量的取值采用“前闭后开区间”写法。
注意:
1.for的初始化、判断、调整三个部分都可以省略**(一旦省略判断部分,那么就意味着判断恒为真)**
//下面列举一个死循环的情况
#include
int main(){
for(;;){
printf("只有坏掉的东西才会停下来,人不会坏,也不能停");
}
return 0;
}
显然,输出的结果是一个死循环:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3iYcich1-1662121651973)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830175933144.png)]
2.循环的嵌套
#include
int main(){
int i=0;
int j=0;
for(;j<10;j++){
for(;i<10;i++){
printf("悟已往之不谏,知来者之可追");
}
}
return 0;
}
这里大家一眼直观认为,该循环会循环100次,那么结果真的是这样吗?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdMZ8iuD-1662121651973)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830180605598.png)]
显然,该循环只循环了10次
分析:当i=0时,内层循环开始j从0加到9,当j=10时跳出内层循环,i++得1而再次进入内层循环后j的值仍等于10内层循环不执行,这里我们省略了初始化的部分才导致这样的结果,修改后如下:
#include
int main(){
int i,j=0;
for(j=0;j<10;j++){
for(i=0;i<10;i++){ //初始化不能随便省略
printf("悟已往之不谏,知来者之可追");
}
}
return 0;
}
printf(“悟已往之不谏,知来者之可追”);
}
}
return 0;
}
这里大家一眼直观认为,该循环会循环100次,那么结果真的是这样吗?
[外链图片转存中...(img-sdMZ8iuD-1662121651973)]
显然,该循环只循环了**10次**
分析:当i=0时,内层循环开始j从0加到9,当j=10时跳出内层循环,i++得1而再次进入内层循环后j的值仍等于10内层循环不执行,这里我们**省略了初始化的部分**才导致这样的结果,修改后如下:
```c
#include
int main(){
int i,j=0;
for(j=0;j<10;j++){
for(i=0;i<10;i++){ //初始化不能随便省略
printf("悟已往之不谏,知来者之可追");
}
}
return 0;
}