自主学习 c语言有关于分支循环语句的笔记
前言
分支语句
- if
- switch
循环语句
- while
- for
- do while
goto语句
第一节 什么是语句?
C语言可分为以下五类:
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句。
可分为以下三类:
- 条件判断语句也叫分支语句:if语句、switch语句;
- 循环执行语句:do whil语句、while语句、for语句;
- 转向语句:break语句、goto语句、continue语句、return语句。
第二节 分支语句(选择结构)
2.1 if语句
#include <stdio.h>
int main()
{
int age = 10;
scanf("%d", &age);
if (age < 18)
printf("青少年\n");
else if (age >= 18 && age < 28)
printf("青年\n");
else if (age >= 28 && age < 40)
printf("中年\n");
else if (age >= 40 && age < 60)
printf("壮年\n");
else if (age >= 60 && age < 100)
printf("老年\n");
else
printf("老寿星\n");
return 0;
}
如果表达式的结果为真,则语句执行。
在C语言中如何表示真假?
0表示假,非 0表示真。
如果条件成立,要执行多条语句,应该使用代码块。
#include <stdio.h>
int main()
{
if(表达式)
{
语句1;
语句2;
}
else
{
语句1;
语句2;
}
}
这里的一对{ }
就是一个代码块
2.1.1 悬空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是和它离得最近的if匹配的。
应规范书写:
int main()
{
int a = 0;
int b = 2;
if (a == 1)
{
if (b == 2)
{
printf("hehe\n");
}
}
else
{
printf("haha\n");
}
return 0;
}
- 规范的使用
{}
可以使代码逻辑更加清楚。 - 代码风格很重要
2.1.2 if书写形式的对比
//代码1
if(condition)
{
return x;
}
return y;
//代码2
if(condition)
{
return x;
}
else
{
return y;
}
//代码3
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
//代码4
int num = 1;
if (5 == num)
{
printf("hehe\n");
}
- 代码1与代码2意思相同;当代码运行到return后,将不再结束执行此部分(整个、循环、判断)的代码
- 代码3与代码4意思相同;if(num == 5)与if (5 == num)表达一致,而使用
if (5 == num)
书写更好(当少打一个=时编译器会报错,而前者的写法少一个=不会报错,但后续容易出bug)
2.1.3 判断一个数是否为奇数
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if (num % 2 != 0) //或 if (num % 2 == 1)
{
printf("奇数\n");
}
else
{
printf("No\n");
}
return 0;
}
2.1.4 输出1-100之间的奇数
int main()
{
int i = 1;
while (i <= 100)
{
if (i % 2 == 1)
printf("%d", i);
i++;
}
return 0;
}
2.2 switch语句
switch语句也是一种分支语句。
常常用于多分支的情况。
比如:
输入1,输出星期一
输入2,输出星期二
输入3,输出星期三
输入4,输出星期四
输入5,输出星期五
输入6,输出星期六
输入7,输出星期日
if…else if…else
的形式太复杂,所以有了不一样的语法形式。
这就是switch语句。语法如下:
switch(整型表达式)
{
语句项;
}
// 语句项就是一些case语句,如下:
case 整型常量表达式:
语句;
表达式必须是整型(也可以是字符类型),不能是浮点数。
2.2.1 在switch语句中的break
在switch语句中,没办法直接实现分支,搭配break使用才能实现真正的分支。
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
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;
case 7:
printf("星期日\n");
break;
default:
printf("选择错误\n");
break;
}
return 0;
}
1.输入1-5,输出的是"weekday";
2.输入6-7,输出"weekend"
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
default:
printf("选择错误\n");
break;
}
return 0;
}
break语句的实际效果是把语句列表划分为不同的分支部分。
2.2.2 default子句
如果表达的值与所有的case标签的值都不匹配
其实也没什么,结构就是所有的语句都被跳过而已。
程序并不会终止,也不会报错,因为这种情况在C中并不认为是个错误。
但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default
子句,把下面的标签
default;
写在任何一个case
标签可以出现的位置。
当switch
表达式的值并不匹配所有case
标签的值时,这个default
子句后面的语句就会执行。
所以,每个switch
语句中只能出现一条default
子句。
2.2.3 练习
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); //m = 5,n = 3
return 0;
}
第三节 循环语句
while
for
do while
//while 语法结构
while(表达式)
循环语句;
3.1 while语句
3.1.1 while语句中的break和continue
while循环中的
break
是用于永久的终止循环
continue
跳过本次循环后面的代码,直接去判断部分,进行下一次循环的判断
break介绍
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
return 0;
}
输出结果:1 2 3 4
continue介绍
案例一
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
return 0;
}
输出结果:1 2 3 4
案例二
int main()
{
int i = 1;
while (i <= 10)
{
i++;
if (5 == i)
continue;
printf("%d ", i);
}
return 0;
}
输出结果:2 3 4 6 7 8 9 10 11
拓:错误列表的解决
3.1.2 getchar() 与 putchar()
3.1.2.1. getchar()
getchar()
- 字符输入函数,没有参数,从输入缓冲区里面读取一个字符 - 「 一次只能读取一个字符 」
EOF
(-1) - end of file 文件结束标志 - 键盘上用 ctrl + z 实现
文档查看
函数的返回值
该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读取错误,则返回 EOF(-1)。所以 getchar()
函数返回值要用 int 整型变量来存储。
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
return 0;
}
运行结果:键盘上输入 abc后按回车,程序会在屏幕上输出 abc\n,输入 def \n,屏幕上会输出 def \n,当按下 ctrl + z 时程序结束运行
getchar
有一个 int 型的返回值。当程序调用 getchar
时,程序就等着用户按键。用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符 \n 也放在缓冲区中),当用户键入回车之后,getchar()
函数才开始从输入缓冲区中每次读取一个字符,getchar
函数的返回值是用户输入的字符的 ASCII 码,若遇到文件结尾 (End-Of-File) 则返回 -1 (EOF),并将用户输入的字符回显到屏幕,如果用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续 getchar 调用读取。也就是说,后续的 getchar 调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才等待用户按键。程序中 while
循环工作时,每一次循环 getchar()
就会从输入缓冲区读取一个字符,然后 putchar
输出,直到遇到了文件结束标志 EOF
,循环判断条件为假,循环才结束。
注意区分 getchar和scanf
对于 scanf
函数,\n
会触发 scanf
读取输入缓冲区的内容,但遇到 \n
或空格 ' '
会停止读取,而 getchar
会直接读取 '\n'
和空格
// scanf多组输入,s,比如2008 6,使用下面语句,直到遇到'\n',停止读取
while(scanf("%d", &n) != EOF)
{
}
getchar使用实例
#include <stdio.h>
int main()
{
char password[20] = { 0 };
//输入密码
printf("请输入密码:>");
scanf("%s", password);
//确认密码
printf("请确定密码(Y/N):");
int ch = 0;
ch = getchar();
//判断是否确认成功
if (ch == 'Y')
{
printf("success!\n");
}
else
{
printf("defeat!\n");
}
return 0;
}
当我们在键盘上输入密码并按下回车后,键盘缓冲区有 “a” “b”“c”“d”“e”“f”“\n"这些字符,此时
scanf
函数开始读取字符串 “a” “b”“c”“d”“e”“f”,遇到”\n"结束读取,这时运行到后面的getchar
函数,getchar 发现缓冲区里还有一个 “\n”,直接将其读取走并会返回给整型变量 ch,这时没有多余的 getchar 函数来接收字符 ‘Y’ 或者 ‘N’ 了,轮到 if 语句来判断时,ch 不等于 ‘Y’ ,所以输出 defeat!
怎么改进,让字符 ‘Y’ 或者 ‘N’ 得以被接收? 可以在ch = getchar();
语句前面增加一条 getchar();
语句,来读取处理掉"\n",这样用来确认密码的那个 getchar 函数就可以接收字符 ‘Y’ 或者 ‘N’ 了
继续思考,如果输入密码时多输了一些无用的字符,又该怎么样处理掉它们呢?比如输入 123456 abcdf\n
需要写一个循环来读取处理掉无用的字符
#include<stdio.h>
int main()
{
char password[20] = { 0 };
//输入密码
printf("请输入密码:>");
scanf("%s", password);
//---清理缓冲区---
int temp = 0;
while ((temp = getchar()) != '\n')
{
;
}
//确认密码
printf("请确定密码(Y/N):");
int ch = getchar();
//判断是否确认成功
if (ch == 'Y')
{
printf("success!\n");
}
else
{
printf("defeat!\n");
}
return 0;
}
运行结果:
// 只打印数字字符,跳过其他字符的。
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
{
continue;
}
putchar(ch);
}
return 0;
}
3.1.2.2 putchar()
将c对应的字符发送至标准输出流stdout,函数原型如下:
int putchar(int c);
函数的参数
putchar()函数只有一个参数c,类型为int型:
- putchar()函数把参数c的值当作无符号整数;
- putchar()函数把参数c的值对256取模后作为实参值;
- 当传递给putchar()函数的实参值在0~127之间时,转换为字符后输出;
- 当传递给putchar()函数的实参值在128~255之间时,没有字符与之对应,输出结果可能和系统有关。
函数的返回值
putchar()函数的返回值类型为int型:
- 输出成功,返回传递给putchar()函数的实际值,范围在0~255之间;
- 输出失败,返回EOF。
3.2 for循环
3.2.1 语法
for(表达式1;表达式2;表达式3)
循环语句;
-
表达式1
表达式1为初始化部分,用于初始化循环变量的。
-
表达式2
表达式2为条件判断部分,用于判断循环什么时候终止。
-
表达式3
表达式3为调整部分,用于循环条件的调整。
案例:打印1-10的数字
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
printf("%d ", i);
return 0;
}
3.2.2 break和continue在for循环中
for循环中也可以出现break和continue,他们的意义和在while循环中是一样的。
但是还是有些差异:
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
break;
printf("%d ", i);
}
return 0;
}
输出结果:1 2 3 4
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
continue;
printf("%d ", i);
}
return 0;
}
输出结果:1 2 3 4 6 7 8 9 10
3.2.3 for语句的循环控制变量
建议:
- 不可在for循环体内修改循环变量,防止for循环失去控制。
- 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
int i = 0;
//前闭后开区间的写法
for (i = 0; i < 10; i++)
{ }
//两边都是闭区间的写法
for (i = 0; i <= 9; i++)
{ }
3.2.4 一些for循环的变种
#include <stdio.h>
int main()
{
//代码1
//for循环中的初始化部分,判断部分,调整部分是可以省略的,但是不建议初学时省略,容易导致问题。
//for循环的判断部分省略意味着这判断会恒成立
for (;;)
{
printf("hehe\n");
}
//代码2
int i = 0;
int j = 0;
//这里打印几个hehe?
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
printf("hehe\n");
}
}
//代码3
int i = 0;
int j = 0;
//如果省略初始化部分,这里打印几个hehe?
for (; i < 10; i++)
{
for (; j < 10; j++)
{
printf("hehe\n");
}
}
//代码4-使用多余一个变量控制循环
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hehe\n");
}
return 0;
}
代码1:死循环
代码2:100个 hehe
代码3:10个 hehe
代码4:2个 hehe
3.2.5 一道笔试题
//问:循环要循环多少次?
int main()
{
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++)
k++;
return 0;
}
循环条件:k=0是将0赋值给k,0为假,所以不进入循环
循环次数:0次
3.3 do…while()循环
3.3.1 语法
do
循环语句;
while(表达式);
案例:打印1-10的数字
int main()
{
int i = 1;
do
{
printf("%d ", i);
i++;
}
while (i <= 10);
return 0;
}
3.3.2 do语句的特点
循环至少执行一次,使用的场景有限,所以不是经常使用。
3.3.3 do while循环中的 break和continue
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
while (i <= 10);
return 0;
}
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
while (i <= 10);
return 0;
}
break输出结果:1 2 3 4
continue输出结果:1 2 3 4 …(死循环)
3.4 练习
- 计算n的阶乘
- 计算
1!+2!+3!+……+10!
- 在一个有序数组中查找具体的某个数字。(讲解二分查找)
- 编写代码,演示多个字符从两端移动,向中间汇聚。
- 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序。
3.4.1 练习参考代码
计算n的阶乘
int main()
{
int i = 1;
int n = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
printf("%d\n", ret);
return 0;
}
计算1!+2!+3!+……+10!
//代码1
int main()
{
int i = 1;
int n = 0;
int ret = 1;
int sum = 0;
for (n = 1; n <= 10; n++)
{
ret = 1;
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
sum = sum + ret;
}
printf("%d\n", sum);
return 0;
}
//代码2
int main()
{
int n = 0;
int ret = 1;
int sum = 0;
for (n = 1; n <= 10; n++)
{
ret = ret * n;
sum = sum + ret;
}
printf("%d\n", sum);
return 0;
}
在一个有序数组中查找具体的某个数字(折半查找算法)
//有序数组 二分查找
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int i = 0;
//求数组的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while (left <= right)
{
//int mid = (left + right) / 2;
int mid = left + (right - left) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("找到了,下标是:%d\n", mid);
break;
}
}
if (left > right)
{
printf("找不到\n");
}
return 0;
}
编写代码,演示多个字符从两端移动,向中间汇聚
//welcome to bit!!!!
//##################
//w################!
//we##############!!
//wel############!!!
#include <Windows.h>
#include <stdlib.h>
int main()
{
char arr1[] = "welcome to bit!!!!";
char arr2[] = "##################";
int left = 0;
int right = strlen(arr2) - 1; //int right = sizeof(arr1)/sizeof(arr1[0])-2;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);
//清空屏幕
system("cls"); //system是一个库函数,可以执行系统命令
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
模拟用户登录情景,并且只能登录三次
//编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序。
#include <string.h>
int main()
{
int i = 0;
char passwork[20] = { 0 };
//假设密码是字符串:abcdef
for (i = 0; i < 3; i++)
{
printf("请输入密码:>");
scanf("%s", passwork);
if (strcmp(passwork, "abcdef") == 0)
{
printf("登录成功\n");
break;
}
else
{
printf("密码错误\n");
}
}
if (i == 3)
{
printf("三次密码输入错误,退出程序\n");
}
return 0;
}
- 比较 2个字符是否相等,不能使用 ==,而应该使用一个库函数:
strcmp
- 如果返回值是 0,表示 2个字符串相等
3.4.2 猜数字游戏实现
//1.电脑产生一个随机数(1-100)
//2.猜数字
//猜大了
//猜小了
//直到猜到了,结束
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("************************************\n");
printf("********** 1.play **************\n");
printf("********** 0.exit **************\n");
printf("************************************\n");
}
//0~RAND_MAX(32767)
void game()
{
int guess = 0;
//1.生成一个随机数
//0~99 --> 1~100
int ret = rand() % 100 +1;//rand:生成随机数的函数
//printf("%d\n", ret);
//2.猜数字
while (1)
{
printf("请猜数字:>");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了\n");
}
else if (guess > ret)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
}
}
//指针
//int* p = NULL;
int main()
{
int input = 0;
//利用time函数生成的时间戳作为随机数的生成起点
srand((unsigned int)time(NULL));//NULL:空指针
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();//猜数字的整个逻辑
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新输入");
break;
}
} while (input);
return 0;
}
知识点总结:
1.rand()函数是伪随机数生成器,用于产生伪随机数,无参数,返回一个int类型且范围在0~RAND_MAX之间的伪随机数。
- RAND_MAX 用来指明 rand() 函数所能返回的伪随机数的最大值;
- C语言标准并未明确规定RAND_MAX的值,但至少是0x7FFF = 32767。
2.在调用rand()函数之前,需要先调用srand()函数为伪随机数生成器提供seed,即设置伪随机序列的起点。
只有seed值是随机的,每次运行程序时才会产生完全不同的伪随机序列。
3.srand()函数
srand()函数的函数原型如下所示:
void srand( unsigned int seed );
srand()函数返回值为空;srand()函数只有一个参数seed
,类型为unsigned int。
关于参数seed说明如下:
- seed相同,则rand()函数禅僧的伪随机序列也相同;
- 如果调用rand()函数之前从未调用过srand()函数,则seed默认值为1,C程序会自动调用srand(1)为rand()函数提供seed;
- 如果调用rand()函数之前曾经调用过srand()函数,则seed为用户指定值。
如何获取随机的seed呢?
答:将time()函数返回值作为seed。
4.time()函数
time_t time(time_t *seconds)
参数
time()函数参数只有一个seconds,类型为time_t*
,即指向time_t类型的指针;time_t类型等价于long long int类型。
返回值
time()函数返回值为time_t类型,返回的是自格林尼治时间1970年1月1日00:00:00时起到当前时刻的时长,时长单位是秒。
使用方式
time()函数有两种使用方式:
将空指针传递给time()函数,并将time()返回值赋给变量t,即:
t = time(NULL)
或
t = time(0)
将变量t的地址作为实参传递给time()函数,函数自动把结果传递给变量t,无需额外的赋值操作。
5.rand+time
int main()
{
int i = 0;
srand((unsigned)time(0));
for (i = 0; i < 10; i++)
{
printf("rand = %d\n", rand());
}
printf("\n");
return 0;
}
说明:
-
time()函数返回值类型为time_t,即long long int类型;
-
srand()函数参数类型为unsigned int类型;
-
当把time()函数返回值作为 srand()函数参数参数时,需要使用
(unsigned)
做数据类型强转。
第四节 goto语句
C语言中提供了可以随意滥用的goto语句和标记跳转的标号。
从理论上goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。
但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层或多层循。
多层循环这种情况使用brak是达不到目的的。它只能从最内层循环退出到上一层的循环,
goto语言真正适用的场景如下:
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
error:
if(disaster)
// 处理错误情况
案例:关机程序
#include <string.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60");
again:
printf("请注意,你的电脑即将在60s内关机,若输入取消即可取消关机操作\n");
scanf("%s", input);
if (strcmp(input, "取消") == 0)
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
// 不用goto的写法
#include <string.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60");
while (1)
{
printf("请注意,你的电脑即将在60s内关机,若输入取消即可取消关机操作\n");
scanf("%s", input);
if (strcmp(input, "取消") == 0)
{
system("shutdown -a");
break;
}
}
return 0;
}