C语言是结构化的程序设计语言。
因此有了顺序结构、选择结构(if、switch、case)、循环结构(while、for、do...while)。
分支语句:if、switch
循环语句:while、for、do...while
goto语句
1.什么是语句?
C语言语句可以分为以下五类:表达式语句、函数调用语句、控制语句、复合语句、空语句
1.表达式语句和函数调用语句
表达式语句比如b=a+2;函数调用语句是对一个函数直接使用并用该函数的名字表示的语句
2.控制语句
控制语句用于程序的执行流程,以实现程序的各种结构方式(C语言支持三种结构:顺序结构、选择结构、循环结构),他们由特定的语句定义符组成,而C语言有9种控制语句。
可以分为以下三类:
1.条件判断语句也叫分支语句:if语句、switch语句;
2.循环执行语句:do while语句、while语句、for语句;
3.转向语句:break语句、goto语句、continue语句、return语句
分支语句(选择语句)
if(表达式)
语句;//1
if(表达式)
语句1;
else
语句2;//2
//多分支
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;//3
如果条件成立,表达式中如果不仅仅只有一条语句,需要使用代码块。(但是建议一般书写if else语句带上大括号。)
#include<stdio.h>
int main()
{
if(表达式)
{
语句列表1;
}
else
{
语句列表2;
}
return 0;
}
表达式的判断问题
在C语言如何判断真假?0表示为真,非0表示为假。
同时要注意在表达式中不可以多个判断,可能会导致错误。例如
if(age<18)
{
printf("未成年");
}
else if(18<= age <30)
{
printf("青年");
}//如果输入20,会输出青年
//如果输入50,也会输出青年
//因为(18<=age)的值为真,所以为1而(1<30)所以就输出"青年"了
正确写法为:
else if(age>=18 && age<30)
多条语句if else 的使用提醒
if (age < 18)
{
printf("未成年\n");
}
else if (age>=18 && age<30)
{
printf("青年\n");
}
else if (age >= 30 && age < 60)
{
printf("中年\n");
}
else if (age >= 60 && age < 90)
{
printf("老年\n");
}
else
{
printf("老寿星\n");
}
虽然有很多if else语句,但是执行只会选择一条进行执行。
同时如果条件的数据层次是严格划分的话,那么就不需要前半部分的条件了,因为已经判断过该范围了,不需要再限定了。例如(age >= 30 && age < 60),只需写成(age < 60)。
悬空else问题
下面代码输出结果是什么呢?
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
答案是:
因为里面else和最近的if组成一个if else语句整体,而不是和外面的if为一个整体。运行程序时,首先进行if条件语句是否等于a==1,如果a不等于1那么无法执行下面的语句,最终什么东西都没有输出。
故可以注意的点是:1.else会与最近的if组成一个整体。2.其次排版也非常重要,空格的合理使用还有缩进 3.同时尽量加上括号书写,避免错误。
if书写形式的对比
//代码1
if (condition) {
return x;
}
return y;
//代码2
if(condition)
{
return x;
}
else
{
return y;
}
//代码1和代码2的表达逻辑是一样的,但是代码1的可读性没有代码2可读性高
//代码3
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
//代码4
int num = 1;
if(5 == num)
{
printf("hehe\n");
}
//代码4可以避免=缺失的错误
补充:return的作用
return可以让该函数结束,同时带回值。
int test()
{
int a = 0;
scanf("%d", &a);
if (a > 5)
return 1;
else
return 2;
printf("hehe3\n");
}
int main()
{
printf("hehe1\n");
int ret = test();
printf("hehe2\n");
return 0;
}//输入a=8则结果为hehe1、hehe2(ret=1)
//return返回了值为1,而且让函数提前结束了,故不论a等于多少,都无法打印hehe3
switch语句
switch语句也是一种多分支语句。常常用于多分支语句。
switch(整形表达式)
{
语句项;//case语句表达式: 语句;
}
例:输入1,输出星期一;输入2,输出星期二.....依次类推
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
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;//在最后一个语句的break理论上是不需要写,但是如果后面有做修改可能会忽略掉,从而导致错误所以建议在目标语句结束加上break收尾
default:
printf("选择错误\n");
break;
}
注意事项:
1、switch括号里的表达式是整形表达式;(如果定义day时int改成float,则会报错)
2、case+空格+整形常量表达式(case后面如果接非整形常量表达式也会报错;但是可以用字符,因为字符的存储是通过ASCII的值存储的,其实是整数,此时switch括号的整形表达式的数据类型可以接char、short、int、long、longlong)如果接多个语句,需要加上大括号。
3、case里面顺序可以乱序,处理时只需按照逻辑进行处理,遇到break结束即可
再例如:星期1-5为工作日,星期6-7为休息日
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("选择错误\n");
break;
}
如果逻辑相同可以把case罗列在一起。
同时从此看出,case是按照某个点进行判断的(是根据day的值,也就是switch括号里的整形表达式),而if是按照范围判断的。
循环语句
- while
- for
- do...while
3.1while循环
如果有同一件事情需要我们重复多次,那么我们可以用while循环。
总结:break在循环中的作用
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
所以:while中的break是用于永久终止循环的。
总结:continue在循环的作用
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,
而是直接跳转到while语句的判断部分。进行下一次循环的入口判断
getchar读取到字符的时候返回的是字符的ASCII码值,如果读取失败,返回的是EOF(EOF的值为-1,ASCII的范围是:0-127)
再看几个代码:
//代码1
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}//这段代码适当的修改是可以来清理缓冲区的
输入函数(scanf、getchar)不是直接从键盘读取数据,而是通过输入缓冲区来读取数据的。
如果缓冲区没有东西,那么调试窗口的光标会一直闪烁等待输入,输入后放入输入缓冲区,你输入A并且按回车确认,此时会打印一个A,之后又把\n放入ch,此时打印\n,所以之后光标在第三行闪烁。
//代码2
#include <stdio.h>
int main()
{
printf("请输入密码:>");
char password[20] = { 0 };
scanf("%s", password);
//清理缓冲区
while (getchar() != '\n')
{
;
}//在\n结束
printf("请确认密码(Y/N):>");
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
运行结果:
运行过程:首先键盘输入的"123456 abc"进入缓冲区,scanf将缓冲区的内容读入在空格前就结束了,只读入了”123456“,但是后面的“ "和"abc”没有读入,而后面的getchar将缓冲区中的“ ”和“123456”清除了,以至于在输入scanf时,ch没有值,而此时的值需要重新键盘输入,若选择输入"Y"则确认成功。
那么如何解决只在空格前并读把所有输入读完呢?可以修改代码
char arr[20]={0};
scanf("%[^\n]",arr);//读取直到\n
scanf("%d",arr);//原
这样就可以在\n之前写入什么,读入什么。
//代码3
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
return 0;
}
此函数的功能:只打印数字字符
for循环
for(表达式1;表达式2;表达式3)
{循环语句}
注:表达式1为初始化部分,用于初始化循环变量的。
表达式2为条件判断部分,用于判断循环时候终止。
表达式3为调整部分,用于循环条件的调整。
int main()
{
int i = 0 ;
for(i=1;i<=10;i++)
{
if(i==5)
continue;//break;
printf("%d",i);
}
//while和for循环中,break始终是终止循环;continue是直接进入下一个循环,对for来说只是不执行后面的语句,但是仍然执行i++(表达式3)。
.1for语句的循环控制变量
注意:1、不要在for循环体内修改循环变量,防止for循环失去控制。
2、建议for语句的循环控制变量的取值采用“前闭后开区间”写法。比如【0,10)因为比较直观
//代码1
int main()
{
int i=0;
for(i=1;i<=100;i++)
{
//程序代码
i=0;//指此类在for中重新对i进行赋值、修改等的操作,尽可能避免重新定义以免出现错误
}
return 0;
}
.2for循环的变种
//代码1 省略for的所有条件
for(;;)
{
printf("hello");//死循环执行
}
//代码2 省略for的初始化部分
int i = 0;
int j = 0;
for(;i<3;i++)
{
for(;j<3;j++)
{
printf("hello");//i=0,j=0,j=1,j=2,j=3;
}
}//打印3次
//因为重新进入循环时未对j进行初始化,导致i重新进入j的值还是为3
//代码3 (与条件2对照)
int i = 0;
int j = 0;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("hello");
}
}//打印9次
//代码4(判断条件可多个)
int x, y;
for (x = 0, y = 0; x<2 && y<3; ++x, y++)
{
printf("hello\n");
}
return 0;
}
注意:1、for循环的初始化,判断,调整三个部分均可以省略。但是如果吧判断部分省略了,意味着判断恒为真,进入死循环。2、for循环内的初始化一定要写,防止出现错误。
例题:
//请问循环要循环多少次?
#include <stdio.h>
int main()
{
int i = 0;
int k = 0;
for(i =0,k=0; k=0; i++,k++)
k++;
return 0;
}//循环0次,“k=0”使得条件为0,即为假,则不进入循环
//一个=为赋值,两个==为判断
do...while语句
do...while()循环的语法
do
{
循环语句;
}
while(表达式);//判断
执行流程图
//break在for、while、do...while、switch中使用
//continue在for、while、do...while中使用
do语句的特点
循环至少执行一次,条件比较特殊,使用场景有限,所以不是经常使用。
练习
1、n的阶乘
#include <stdio.h>
int main()
{
int n = 0;
//输入
scanf("%d", &n);
//计算n的阶乘
int i = 0;
int ret = 1;
for (i = 1; i <= n; i++)
{
ret *= i;
}
printf("%d\n", ret);
return 0;
}
2、.计算 1!+2!+3!+……+10!
//代码1
int main()
{
int n = 0;
//输入
//计算n的阶乘
int i = 0;
int ret = 1;
int sum = 0;
//1+2+6 = 9
for (n = 1; n <= 10; n++)
{
ret = 1;
//计算n的阶乘
for (i = 1; i <= n; i++)
{
ret *= i;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
//代码2
int main()
{
int n = 0;
//输入
//计算n的阶乘
int i = 0;
int ret = 1;
int sum = 0;
//1+2+6 = 9
for (n = 1; n <= 10; n++)
{
ret *= n;//用不断变大的ret与n乘,减少不必要的循环
sum += ret;
}
printf("%d\n", sum);
return 0;
}
3、在一个有序数组中查找具体的某个数字n(有序数组可以用二分法查找)
//二分法查找(折半查找),前提是数组有序
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 14;
int sz = sizeof(arr) / sizeof(arr[0]);//计算给定数组长度
int left = 0;
int right = sz - 1;
int flag = 0;//作为一个标志,用于检验是否找到
while (left <= right)
{
int mid = (left + right) / 2;//这里是为了每次找到中间的值来确定被查找数字的区间
//同时这里还有另一种写法:
//int mid = left + (right - left) / 2
if (arr[mid] > k)
{
right = mid - 1;
}
else if (arr[mid] < k)
{
left = mid + 1;
}
else
{
printf("找到了,该数下标为%d", mid);
flag = 1;
break;//结束了及时跳出
}
}
if (flag == 0)
{
printf("没有找到!");
}
return 0;
}
第二种取中间值的思路:
两个值不直接相加,是为了防止在某些情况下发生溢出的现象。这种方法通过将两者多出的部分进行平均处理,来避免数字溢出带来的影响。
4、编写代码,演示多个字符从两端移动,向中间汇聚。
编写代码,演示多个字符从两端移动,向中间汇聚。
#include<stdio.h>
#include <string.h>
#include <windows.h>//Sleep函数头文件
int main()
{
char arr1[] = "welcome to bit!!!!";
char arr2[] = "******************";
int left = 0;
int right = strlen(arr1)-1;
while (left<=right)//或者用for循环
//用for循环写的,这里的变化就是定义写在for里面,判断条件放中间,加加减减放到最后
//for(left = 0, right = strlen(arr1) - 1;left <= right; left++, right--)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//让程序慢一点执行
system("cls");//清屏
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。
// 5. 编写代码实现,模拟用户登录情景,并且只能登录三次
//(只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
char password[20] = { 0 };
int flag = 0;
for (i = 0; i < 3; i++)
{
printf("请输入密码>:");
scanf("%s", password);//数组名本身就是地址,不需要取地址
if (strcmp(password, "123456") == 0)//判断
{
printf("密码正确!\n");
flag = 1;
break;
}
else
{
printf("密码错误\n");
}
}
if (flag == 0)
{
printf("三次密码均错误,退出程序!");
}
return 0;
}
6、猜数游戏实现
//猜数字游戏实现
//电脑生成一个1-100的随机数,
//猜大了 就告诉你:猜大了,猜小了 就告诉你:猜小了;猜对了就告诉你:恭喜你,猜对了
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("******************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("******************\n");
}
void game()
{
int ret = rand()%100+1;//1.生成随机数
//rand函数会返回一个0-32767之间的随机数
//“Use the srand function to seed the pseudorandom-number generator before calling rand.”
//所以需要调用srand函数生成伪随机数生成器种子
//同时为了避免对函数多次调用产生的随机数相近,把随机数生成器放入主函数中,每次猜数只调用一次即可
// %100是可以取0-99之间的数字,然后加一;可以取到1-100之间的数字
//2.猜数字
int guess = 0;
while (1)
{
printf("请猜数字:>");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了\n");
}
else if (guess > ret)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//设置随机数的生成器
do
{
menu();
printf("请选择>:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
1.生成随机数。
随机数的生成需要我们使用rand函数,rand函数会返回一个0-32767之间的随机数,rand在用法中:“Use the srand function to seed the pseudorandom-number generator before calling rand.”所以需要调用srand函数生成伪随机数生成器种子。
然而srand生成随机数,需要srand括号里是一个随机数,我们可以引用时间戳。因为每一个不同的时间都会对应一个时间戳,我们可以运用时间戳来让srand函数产生随机值。而获得时间戳需要调用time函数,而该函数头文件为:include <time.h>,该函数返回的值是一个时间戳,而这个时间戳可以作为一个随机值。
2.其次就是需要注意取1-100之间的数,那么就需要取余0-99之间的数。