【前言】本系列(初阶)适用于初学者课前预习或者课后复习资料,包含了大部分基础知识点梳理与代码例方便看官理解,有问题请指出。本人邮箱地址:p772307283@outlook.com
【概要】
分支语句
if switch
循环语句
while for do while
goto语句
目录
1.语句
C语言大概有以下五类语句
1.表达式语句。
2.函数调用语句。
3.控制语句。
4.符合语句。
5.空语句。
而后面主要介绍的就是控制语句了。
控制语句用以控制程序的执行流程,用来实现程序的各种结构方式。c语言是一门结构化的程序设计语言,支持顺序结构,选择结构,循环结构三种结构。其实人生也是这样,在不断向前走,遇到分支踌躇不前,或者待在原地转圈。这些结构由九种控制语句组成。
条件判断语句: if语句,switch语句。
循环执行语句:for语句,do while语句,while语句。
转向语句:break语句,goto语句,continue语句,return语句
2.条件判断语句
人生处处面临选择,选择努力就有可能拿到好offer,但如果选择摆烂就有可能以后去摊煎饼果子了。
2.1 if语句
//单分支
if (表达式)
语句;
//双分支
if (表达式)
语句;
else
语句;
//多分支
if (表达式)
语句;
else if(表达式)
语句;
else
语句;
这就是if语句的基本结构。if里面的表达式如果为真,则执行语句,如果为假则跳出if语句所管辖的范围。在c语言中规定0为假,非0为真。
单分支的运用
int age = 15;
if (age < 18) {
printf("未成年");
}
但是一旦age大于了18就没有显示了,设计并不完善。
双分支
int age = 19;
if (age < 18) {
printf("未成年");
}
else {
printf("成年");
}
若age大于18则会输出成年字样。那么如果我们并不满意太少的age分类呢?
那我们还可以使用多分支的结构。
int age = 77;
if (age < 18) {
printf("未成年");
}
else if (age >= 18 && age < 30) {
printf("青年");
}
else if (age >= 30 && age < 50) {
printf("中年");
}
else if (age >= 50 && age < 120) {
printf("老年");
}
else
{
printf("输入正确数据 ");
}
以上就是简单的分支语句的使用。并且我们使用了代码块{}来使得程序更加的清晰。我建议每次写分支都加上{},如果不加上{}可能会出现意料之外的小错误,也会对别人看你的代码时产生误解。
int a = 0;
int b = 0;
if (a == 1)
if (b == 2)
printf("1");
else
printf("2");
第一眼告诉你的答案是多少呢,是2吗?
答案是什么都没有,如果你将这段代码放在vs上,会发现VS自动帮你调整了else的位置,放到了第二个if的正下方,这就说明了else的匹配是与其最近的if匹配的。这就从侧面说明了一个好的代码风格是非常重要的,在使用if语句时使用适当的{}可以让你的代码更加清晰,风格更加明了,毕竟代码不只是写给自己看的,还要给别人看。所以笔者不建议使用dev++这种老旧的编译器来进行代码书写,对初学者的代码风格养成不好。
在看一个因为代码风格不好容易引起误解的例子。
if (true) {
return 1;
}
return 2;
如果true为真,则会返回1,同时也不会返回2,当返回1后这串代码执行就已经完成。但总会给人造成误解,认为就算true为真,在返回了1后还会返回2这个值,这是语法不允许的。
建议更改为
if (true) {
return 1;
}
else {
return 2;
}
2.2switch语句
比如我们想完成一个这样的代码功能,当输入1,输出星期一,输入2,输出星期二,直到输入7,输出星期天。
这样的程序实现如果还是使用多分支的语法结构,会显得非常复杂,且不利于查看。
这个时候就要用到switch语句了。
switch(整型表达式)
{
语句项;
}
注意只能是整型表达式,不可以是其他。
里面的语句项则是一些case语句。
case 整型常量表达式;
语句;
并且在switch语句中,case决定入口,break决定出口。 所以只有搭配了break才可以实现真正的分支。
int day = 0;
scanf("%d", &day);
switch (day) {
case 1:
printf("monday\n");
break;
case 2:
printf("tuesday\n");
break;
case 3:
printf("wednesday\n");
break;
case 4:
printf("thursday\n");
break;
case 5:
printf("friday\n");
break;
case 6:
printf("saturday\n");
break;
case 7:
printf("sunday\n");
break;
}
这就是一个简单的switch语句。当day等于1,2,3..... 时,走向不同的case入口。 注意,在进入case入口后,程序会一直执行,直到遇到出口break。
比如如果case 1后面没有break,当day等于1,会输出monday和tuesday,在case 2后面遇到break才会终止并跳出。
如果输入了不符合1到7的数呢?则可以利用default。
default:
printf("error\n");
break;
当我们不需要输出如此清晰,只需要weekday与weekend的区别时,就可以利用break的出口机制,简化代码。
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的效果就是将语句列表划分为不同的分支部分。
来看一道练习加深你对switch语句的理解。
int n = 1;
int m = 2;
switch (n) {
case 1:
m++;
case 2:
n++;
case 3:
switch (n) {
case 1:
n++;
case 2:
m++;
n++;
break;
}
case 4:
m++;
break;
default:
break;
}
printf("m=%d,n=%d", m, n);
输出结果是m=5,n=3
原理如下:
输入m=2,n=1后,进入了第一个switch的case 1语句,此时m++变成了3,同时因为没有break,所以程序继续向下走,使n++,变成 2,也是没有break,所以走向了case 3的switch嵌套,此时n-2,所以进入case 2语句,m与n分别++,变成了4,3,此时遇到break,跳出了嵌套的switch,继续进行第一个switch,进入case 4,使m++,变成5,此时遇到break,程序终止,跳出第一个switch语句。
3.循环语句
3.1while循环
当我们使用if时,会发现其只能执行一次,但是生活中会有很多实际的例子需要我们去多次的实现
那就要用到while循环了。
while(表达式)
循环语句;
当表达式为真,就会进入循环,执行循环语句。
现在来看一个打印1-10的例子。
#include<stdio.h>
int main() {
int i = 1;
while (i <= 10) {
printf("%d ", i);
i++;
}
return 0;
}
代码的实现非常简单,i小于等于10时进入循环,打印后加1,再回到while进行判断是否进行下一次循环。
在while语句中也会遇到break和continue。
先上结论
break:在循环中遇到break,就停止后期的所有循环,直接将循环进行永久终止。
代码示例
int main() {
int i = 1;
while (i <= 10) {
if (i == 5) {
break;
}
printf("%d ", i);
i++;
}
}
输出为1 2 3 4
当i等于5时,进入了循环,同时进入了if的判断,读到了break,终止了整个循环。后面也就不再执行了。
continue
continue用于终止本次循环,直接跳转到下一次while循环判断部分。
代码示例
int main() {
int i = 0;
while (i < 10)
{
i++;
if (i == 5)
{
continue;
}
printf("%d ", i);
}
return 0;
}
运行后结果为1 2 3 4 6 7 8 9 10
唯独缺少了5,因为为5时进入了if的判断,执行了continue,终止本次循环,直接回到了while再次进行判断。
代码示例
int main() {
int ch=0;
while ((ch = getchar()) != EOF) {
putchar(ch);
}
return 0;
}
这里简单介绍一下getchar。
getchar的作用是从键盘中读入一个字符,也就是从输入缓冲区中拿取,每次只能拿取一个字符。
当我们输入比如A后,要按下回车,这个时候回车(\n)也被放入了输入缓冲区,当getchar读走了A时,会顺着循环继续读取缓冲区中剩下的回车。所以实际上循环进行了两次,分别通过putchar打出了A和\n。所以我们发现下标在输出的A的下一行。
再来看一个代码示例熟悉getchar的基础用法。
int main() {
char charr[20] = { 0 };
char ch;
scanf("%s", charr);//向字符数组中输入一串字符
printf("确认字符串输入是否正确(y)?");
ch = getchar();
if (ch == 'y') {
printf("正确");
}
else
{
printf("错误");
}
}
在运行时会发现在输入一串字符串后会立即打印出错误的字样。根本没有给我们输入y的机会。
和上面是同理,按下的回车被下面的getchar接收了。
所以我们只需要在增添一个getchar消化掉回车就好了。在scanf后面加一个getchar即可。
int main() {
char charr[20] = { 0 };
char ch;
scanf("%s", charr);//向字符数组中输入一串字符
getchar();
printf("确认字符串输入是否正确(y)?");
ch = getchar();
if (ch == 'y') {
printf("正确");
}
else
{
printf("错误");
}
}
在了解一下scanf的机制,scanf函数读到空格和回车都不会再继续读取。
所以如果我们打出的字符串是类似于dwda dw12这样的字符串,上面的代码又需要进一步在getchar上下功夫。
while (getchar() != '\n') {
;
}
这样只能保证对字符串正确与否的判断正常进行,但是输入的字符串从空格后就不会再被读取了。所以我们只能得到dwda。
3.2for循环
for语法
for(exp1 ;exp2; exp3)
循环语句;
exp1用于初始化循环变量
exp2用于判断循环终止
exp3用于调整循环条件
代码示例
int main() {
int i = 0;
for (i = 1; i <= 10; i++) {
printf("%d ", i);
}
return 0;
}
i=1为初始化的循环变量,i<=10为判断循环是否终止的条件,i++为循环条件的调整。
还记得i++的用法吗?先用后加,所以进入循环的i是1,第一次循环完成后i在自增为2
和while循环相比较,使得while循环的三个条件分隔太远,所以像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,在i=5时执行break直接中断整个for循环。
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
与while循环结果一样。
同时也存在一些for循环的变种写法,需要大家了解。
for (;;) {
printf("1");
}
死循环打印1
int i = 0;
int j = 0;
for(i=0;i<3;i++)
{
for (j = 0; j < 3; j++)
{
printf("1 ");
}
}
嵌套循环,打印了9个1,i=0时执行了三次j从0到2的打印,i也从0到2,所以是九次。
int i = 0;
int j = 0;
for(;i<3;i++)
{
for (; j < 3; j++)
{
printf("1 ");
}
}
省略初始化的嵌套循环。这样只能打印3次。开始时,i为0,小于3,可以进入循环,同理j=0,进入嵌套循环,打印1,同时j自增,直到为3,跳出本次嵌套循环,回到i的循环。i此时为2,但往下进行时j已经等于3了无法进行嵌套的循环,所以跳过再次进入i的循环直到结束。
其实本质上就是因为j没有初始化,导致其无法在第二次i的循环中重置为1
3.3do while循环
语法结构
do
循环语句;
while(表达式);
其实do while和while循环也无太大差别,但是do while循环至少会执行一次。
也就是先执行一次,再去判断循环是否继续。
而while循环是先判断再执行,此外二者基本无太大差别了。
代码示例
int i = 1;
do
{
printf("%d ", i);
i++;
} while (i <= 10);
打印结果为1 2 3 4 5 6 7 8 9 10
do while循环中也有break和continue。
break
int i = 1;
do
{
if(i==5)
{
break;
}
printf("%d ", i);
i++;
} while (i <= 10);
打印结果为1 2 3 4
continue
int i = 1;
do
{
i++;
if(i==5)
{
continue;
}
printf("%d ", i);
} while (i <= 10);
打印结果为1 2 3 4 6 7 8 9 10,结果中没有5
如果i++的位置仍然放在上一个代码原位置,会在一些编译器造成死循环。
4.循环相关练习
4.1计算n的阶乘
int i = 1;
int ret = 1;
int n = 0;
scanf("%d", &n);
for (i = 1; i <= n; i++) {
ret = ret * i;
}
printf("%d", ret);
4.2计算1到10所有数的乘阶和
int sum = 0;
for (int i = 1; i <= 10; i++)
{
int ret = 1;
for (int j = 1; j <= i; j++)
{
ret = ret * j;
}
sum += ret;
}
printf("%d", sum);
4.3二分查找
题目示例:在一个有序数组中寻找某个数字n
二分查找必须要求其具备某种大小顺序,否则无法进行。
比如现在有一个数组包含了1-10共10个数字。我们需要查找其中是否有7这个数字。
二分查找的思想很简单,就是每次砍掉一半,所以二分查找的效率很高,比如你需要在一个上亿的有序数组里查找,运用二分查找可以在开始就直接砍掉一半,并循环反复。
我们将数组第一位的下标定为left,最后一位的下标定为right
再将需要查找的数字定为key
再定义一个mid为0(下一步会用)
那么此部分代码为
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int right = 9;
int left = 0;
int key = 7;
int mid=0;
接下来让mid成为数组中间那个数字的下标。
二分查找核心思想
因为是有序数组,即将key与arr[mid]作比较,如果key比他大,则说明key在以mid为分隔符的下半段里。若key比他小,则说明key在上半段里。若相等那就直接找到了。
若在下半段,则将left指向mid+1的位置,若在上半段,则将right指向mid-1的位置。
然后再进行key与新的mid(新right或者新left与旧的相加再除以2)进行比较,如此循环。
笔者文笔略差,看官可以自己画一个数组来按照步骤进行操作方便理解。
查找终止的条件无非就是找到了或者left与right交叉。
所以完整代码如下
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int right = 9;
int left = 0;
int key = 7;
int mid = 0;
int flag=0;
while (left <= right)
{
mid = (right + left) / 2;
if (arr[mid] > key)
{
right = mid - 1;
}
else if(arr[mid]<key)
{
left = mid + 1;
}
else
{
printf("找到了");
flag += 1;
break;
}
}
if (flag == 0)
{
printf("找不到");
}
return 0;
4.4字符移动
编写一段代码使多字符从两端滑动,同时向中间汇聚。
#include<stdio.h>
#include<stdlib.h>
int main()
{
char arr1[] = "this is amazing!";
char arr2[] = "****************";//创建两个数组,使其具有同样长度。
int left=0;
int right = strlen(arr1) - 1; //同二分查找相同,使用数组下标来控制字符串的改动。
printf("%s", arr2);
while (left<=right) //循环条件也同二分查找,当两下标移动到相同位置则停止打印。
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
printf("%s", arr2);
Sleep(1000); //使用sleep函数使打印更具层次感
system("cls"); //执行清理屏幕的指令
}
return 0;
}
4.5 输入密码
条件:只有三次输入机会,错误则令程序停止
#include<stdio.h>
int main()
{
char password[20] = { 0 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
printf("input the password\n");
scanf("%s", password);//数组名已经是地址,无需取址符&
if ((strcmp(password, "123456")) == 0)
{
printf("right!\n");
break;
}
else
{
printf("try it again\n");
}
}
if (i == 3)
printf("exit\n");
return 0;
}
4.6实现猜数游戏
游戏功能:随机提供一个1-100内的数字,每次猜测后会给出大了还是小了,直到猜中为止。
本题对循环并无太大要求,而是随机数的给出。
c语言中内置的库函数可以实现随机数,rand
int a;
for (int i = 0; i < 3; i++)
{
a = rand();
printf("%d\n", a);
}
但是当你使用rand去创造数字,会发现每一次程序重新运行创建的数字都是一样的。
这样显然背离了游戏的初衷。
所以在使用rand函数时还需要使用一个前置函数srand
srand用于设置随机数生成器。
如果置srand(0),会使每一次循环所得到的a的值都是同一个。
所以想要得到一个随机数,我们需要使srand里的数也是随机的,用随机来创造随机。
各位看官可听说过时间戳吗?srand就可以利用时间戳来实现随机给出数字。
时间是随时变化的,刚好可以用来置srand的值。
srand((unsigned)time(NULL));
在c语言中,time函数返回时间戳。至于为什么要用unsigned ,感兴趣的看官可以自行百度。
不加会报错哦。
到这里一个随机数的给出器就基本完成了。接下来就是完善整个程序。
#include<stdio.h>
#include<stdlib.h>
void menu()
{
printf("*****1 play game*****\n");
printf("*****0 game over*****\n");
}
void game()
{
int num = rand() % 100 + 1;//任何数除以100余的数都在1-99之间,再加1,即得1-100
int b;
while (1)
{
printf("input your number\n");
scanf("%d", &b);
if (b > num)
{
printf("your num is bigger\n");
}
else if (b < num)
{
printf("your num is smaller\n");
}
else
{
printf("right!\n");
break;
}
}
}
int main()
{
int a = 0;
srand((unsigned)time(NULL));
do
{
menu();
printf("choose\n");
scanf("%d", &a);
switch (a)
{
case 1:
game();
break;
case 0:
break;
default:
printf("error,try it again");
}
} while (a);
return 0;
}
5.goto语句
goto语句在实际操作中使用非常少。最常见的用法也只是终止程序在某些深入嵌套得到结构处理过程。‘也就是跳出多层循环。因为break只能跳出一层最近的循环,而goto通过恰当设置可以跳出多层。
goto 也就是去往哪里,比如goto a,程序就会跳至a:这个地方执行。
代码示例:
不按要求输入就关机。
#include<stdio.h>
int main()
{
char a[20] = "";
system("shutdown - s - t 60"); //关机指令为shutdown -s,-t则是设置在多少秒后
again:
printf("即将关机,请输入123456停止关机程序\n");
scanf("%s", a);
if ((strcmp(a, "123456")) == 0)
{
system("shutdown -a"); //取消关机指令
}
else
{
goto again;
}
return 0;
}
以上就是c语言常用的几种语句啦。写文不易,希望看官能留下你的赞。给俺更多动力。