C语言初级篇-----循环语句详解
目录
循环语句
一、while循环
二、for循环
三、do...while循环
四、循环语句练习
getchar 与 putchar
总结
循环语句
if 语句:当条件满足的情况下,if语句后的语句执行,否则不执行。但是这个语句只会执行一次。
生活中很多的实际的例子是:同一件事情我们需要完成很多次。
C语言中给我们引入了循环语句。
C语言中有三种循环语句:
- while循环
- for循环
- do...while循环
一、while循环
1、语法结构
while(表达式)
{
循环语句;
}
2、while循环的执行流程
3、while循环的使用
打印1~100之间的整数
#include<stdio.h>
int main()
{//打印1~100int i = 1;while (i <= 100){printf("%d ", i);//每打印10个数换行if (i % 10 == 0){printf("\n");}i++;}return 0;
}
运行结果:
4、while循环中的break和continue
(1)break
break在循环中的作用:
在循环中只要遇到break,就终止循环(跳出循环)。
while循环遇到break
#include<stdio.h>
int main()
{int i = 1;while (i <= 100){if (5 == i)break;printf("%d ", i);i++;}return 0;
}
运行结果:
代码分析:
当 i 等于5时程序执行break语句,while循环中遇到break,就会跳出循环不会再执行break后的语句。所以最后只打印了 1 2 3 4
(2)continue
continue在循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到循环语句的判断部分。进行下一次循环的入口判断。
while循环遇到continue
#include<stdio.h>
int main()
{int i = 1;while (i <= 100){if (5 == i)continue;printf("%d ", i);i++;}return 0;
}
运行结果:
代码分析:
当 i 等于5时程序执行continue语句,while中遇到continue,本次循环中continue后边的代码不会再执行,直接跳转到while语句的判断部分。但是i++;这条语句在continue后面,所以 i 的值不会再发生变化,i 的值一直都是5。所以程序进入了死循环,最终打印 1 2 3 4....(4的循环)一直循环。
二、for循环
1、语法结构
for(表达式1;表达式2;表达式3)
循环语句;
每个表达式的作用:
- 表达式1:表达式1为初始化部分,用于初始化循环变量的。
- 表达式2:表达式2为条件判断部分,用于判断循环时候终止。
- 表达式3:表达式3为调整部分,用于循环条件的调整。
2、for循环的执行流程
3、for循环的使用
打印1~10之间的整数
#include<stdio.h>
int main()
{int i = 0;for (i = 1; i <= 10; i++){printf("%d ", i);}return 0;
}
运行结果:
4、for循环中的break和continue
(1)break
#include<stdio.h>
int main()
{int i = 0;for (i = 1; i <= 10; i++){if (5 == i){break;}printf("%d ", i);}//打印完以后换行printf("\n");return 0;
}
运行结果:
代码分析:
当 i 等于5时程序执行break语句,for循环中遇到break,就会跳出循环不会再执行break后的语句。所以最后只打印了 1 2 3 4
(2)continue
#include<stdio.h>
int main()
{int i = 0;for (i = 1; i <= 10; i++){if (5 == i){continue;}printf("%d ", i);}//打印完以后换行printf("\n");return 0;
}
运行结果:
代码分析:
当 i 等于5时程序执行continue语句,for循环中遇到continue,本次循环中continue后边的语句不会再执行,直接跳转到for循环中的表达式3(i++)再执行表达式2进行判断(满足条件进入循环)。执行表达式3以后 i 的值变成了6(6不等于5)循环中 if 的条件不成立,程序往下执行输出6,再执行表达式3(i++)再执行表达式2直到表达式2的条件不成立,程序跳出循环。最后输出 1 2 3 4 6 7 8 9 10。
5、for语句的循环控制变量
建议:
- 不可在for 循环体内修改循环变量,防止 for 循环失去控制。
- 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
(1)如果在for循环内修改循环变量,会造成死循环
#include<stdio.h>
int main()
{int i = 0; //不好的习惯for (i = 1; i <= 10; i++){i = 0;printf("hello\n");}return 0;
}
运行结果
(2)for语句的循环控制变量的取值采用“前闭后开区间”
#include<stdio.h>
int main()
{int i = 0;//代码1printf("代码1:\n");for (i = 0; i < 10; i++){printf("hello\n");}printf("-------------------\n");//代码2printf("代码2:\n");for (i = 0; i <= 9; i++){printf("hello\n");}return 0;
}
运行结果:
代码1采用了“前闭后开区间”写法,代码2前后都是闭区间,代码1与代码2的执行结果相同,但是代码1的可读性更好一些。
这里只是建议写成将循环的控制变量的取值写成“前闭后开区间”,根据个人习惯使用。
6、for循环的变种
#include<stdio.h>
int main()
{for (;;){printf("hello\n");}return 0;
}
- for循环的初始化(表达式1)、判断(表达式2)、调整(表达式3)三个部分可以省略。
- 判断部分(表达式2)如果省略,意味着判断恒为真,就构成了死循环
如果条件允许,不建议省略for循环的3个表达式。
如果省略了for循环的表达式,得到的可能不是预期的结果
#include<stdio.h>
int main()
{int i = 0;int j = 0;for (i=0;i<3;i++){for (j = 0; j < 3; j++){printf("hello\n");}}return 0;
}
运行结果:
代码分析:
for循环是可以嵌套的。i = 0时执行里面的for循环输出3个hello;i = 1时执行里面的for循环输出3个hello;i = 2时执行里面的for循环输出3个hello,然后跳出循环。3*3=9,最后打印了9个hello。
如果省略两个for循环中的表达式1,结果会是怎样的呢?
#include<stdio.h>
int main()
{int i = 0;int j = 0;for (;i<3;i++){for (; j < 3; j++){printf("hello\n");}}return 0;
}
运行结果:
代码分析:
- i = 0 时进入循环执行里面的for循环(j=0 输出一个hello,再执行j++,再判断,直到 j 为3时跳出循环)输出3个hello。
- 当i = 1时进入循环,此时里面的for循环 j 没有初始化(此时值为3),j 值为3跳出里面的循环。程序再执行i++。当 i = 2时进入循环,此时里面的for循环 j 没有初始化(此时值为3),j 值为3跳出里面的循环。最后程序只输出了3个hello。
for循环中,表达式1、表达式2、表达式3,可以同时写多个变量。
#include<stdio.h>
int main()
{int i = 0, j = 0;for (i = 0, j = 0; i < 3 && j < 5; ++i, j++){printf("hello\n");}return 0;
}
了解了for循环以后,可以发现for循环与while循环中都存在三个必须条件(三个表达式)。但由于语法结构,使得while循环中的三个表达式太过于分离,这样在修改代码的时候就不太方便,而for循环中的三个表达式是放在一起的。所以for循环的语法结构更好一些,使用频率也是最高的。
做一道题,看看是否真的理解了for循环
下面这段代码循环了几次?
#include<stdio.h>
int main()
{int i = 0;int k = 0;for (i = 0, k = 0; k = 0; i++, k++){k++;printf("hello\n");}return 0;
}
因为表达式2(判断条件)是k赋值为0,所以循环的条件为假不会进入到循环里,所以循环次数为0。
三、do...while循环
1、语法结构
do
循环语句;
while(表达式);
2、do...while循环的执行流程
3、do...while循环的使用
打印1~10之间的整数
#include<stdio.h>
int main()
{int i = 1;do{printf("%d ", i);i++;} while (i <= 10);return 0;
}
运行结果:
4、do...while循环中的break和continue
(1)break
#include<stdio.h>
int main()
{int i = 1;do{if (i == 5)break;printf("%d ", i);i++;} while (i <= 10);printf("\n");return 0;
}
运行结果:
代码分析:
当 i 等于5时程序执行break语句,do...while循环中遇到break,就会跳出循环不会再执行break后的语句。所以最后只打印了 1 2 3 4
(2)continue
#include<stdio.h>
int main()
{int i = 1;do{if (i == 5)continue;printf("%d ", i);i++;} while (i <= 10);printf("\n");return 0;
}
运行结果:
代码分析:
当 i 等于5时程序执行continue语句,do...while中遇到continue,本次循环中continue后边的代码不会再执行,直接跳转到do...while语句的判断部分。但是i++;这条语句在continue后面,所以i 的值不会再发生变化,i 的值一直都是5。所以程序进入了死循环,最终打印 1 2 3 4.....(4的循环)一直循环。
5、do...while循环的特点
循环至少执行一次,使用的场景有限,所以不是经常使用。
四、循环语句练习
1、计算n的阶乘
- 输入 n
- 产生1~n的数字,累积乘在一起
- 打印出来
#include<stdio.h>
int main()
{int n = 0;int i = 0;int ret = 1;scanf("%d", &n);for (i = 1; i <= n; i++){ret *= i;}printf("%d\n", ret);return 0;
}
这里的n不要输入的太大,int类型存储的数据是有限的。
2、计算1~10的阶乘和
#include<stdio.h>
int main()
{int i = 0;int j = 0;int t = 1;int sum = 0;for (i = 1; i <= 10; i++){t = 1;for (j = 1; j <= i; j++){t = t * i;}sum += t;}printf("%d\n", sum);return 0;
}
这种算法程序执行效率太低
代码优化:
#include<stdio.h>
int main()
{int i = 0;int t = 1;int sum = 0;for (i = 1; i <= 10; i++){t *= i;sum += t;}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 = 7;int i = 0;for (i = 0; i < 10; i++){if (arr[i] == k){printf("找到了,下标是%d\n", i);break;}}if (10 == i){printf("找不到\n");}return 0;
}
这种方法是将k的值与数组中的元素一个一个的进行比较。这种方法虽然能解决这个问题,但是效率太低。
方法二(二分查找):
简单说一下二分查找:
- 在一个有序的数组中,先查找中间元素。数组中有10个数,数组下标为:0~9。
- 如果待查找的元素比中间元素小,就将查找的范围缩小一半在中间元素的左边查找(此时查找的数组左下标为0,右下标为中间元素的下标-1)。
- 如果待查找的元素比中间元素大,就将查找的范围缩小一半在中间元素的右边查找(此时查找的数组左下标为中间元素的下标+1,右下标为9)。
这只是进行了一次二分查找,如果要在数组中找到待查找的数,就要进行多次二分查找。
循环的结束条件为 左下标>右下标(因为当左下标>右下标时表示整个数组已经查找完)。
- 如果找到了就输出:找到了,并输出该元素的下标。
- 如果没找到就输出:没找到。
放一张图帮助理解
代码:
#include<stdio.h>
int main()
{int arr[] = { 10,20,30,40,50,60,70,80,90,100 };int k = 70;int sz = sizeof(arr) / sizeof(arr[0]);int left = 0;int right = sz - 1;int mid = 0;while (left <= right){mid = (left + right) / 2;if (k > arr[mid]){left = mid + 1;}else if (k < arr[mid]){right = mid - 1;}else{printf("找到了,下标为%d\n", mid);break;}}if (left > right){printf("找不到\n");}return 0;
}
int sz = sizeof(arr) / sizeof(arr[0]); 计算数组元素个数
- sizeof(数组名)计算的是数组的总大小,单位是字节。
- arr数组中总共有10个元素,每个元素都是整型占4个字节空间,sizeof(arr)计算出来就是40。
- sizeof(arr[0])计算的是第一元素的大小(4)。
- sz = sizeof(arr) / sizeof(arr[0]) = 40 / 4 = 10
4、编写代码,演示多个字符从两端移动,向中间汇聚。
要实现的过程:两个字符串
arr1: Hello World!
arr2: ############
- H##########!
- He########d!
- Hel######ld!
- ……
- Hello World!
代码:
#include<stdio.h>
#include<String.h>
int main()
{char arr1[] = "Hello World!";char arr2[] = "############";int left = 0;int right = strlen(arr1) - 1; //strlen - 求字符串长度while (left <= right){arr2[left] = arr1[left];arr2[right] = arr1[right];printf("%s\n", arr2);left++;right--;}return 0;
}
可以将程序运行的结果以动态的形式展示
#include<stdio.h>
#include<String.h>
#include<windows.h>
int main()
{char arr1[] = "Hello World!";char arr2[] = "############";int left = 0;int right = strlen(arr1) - 1; //strlen - 求字符串长度while (left <= right){arr2[left] = arr1[left];arr2[right] = arr1[right];printf("%s\n", arr2);//windows 提供的函数Sleep(1000);//睡眠函数 - 单位是毫秒system("cls");//执行系统命令 - cls:清空屏幕left++;right--;}printf("%s\n", arr2);return 0;
}
感兴趣的朋友可以运行一下看看代码执行的结果
getchar 与 putchar
输入可以使用scanf 函数,如果想要输入字符也可以使用getchar函数(getchar函数只能用来输入字符)。
getchar - 获取(输入)一个字符
从键盘中获取一个字符,通过返回的方式带回一个值(这个值就是获取的字符),返回值类型为 int 。
getchar 函数的使用
#include<stdio.h>
int main()
{int ch = 0;//scanf("%d",&ch);//输入a的ASCII码值(97)ch = getchar();printf("%c\n", ch);//%c - 打印字符return 0;
}
运行结果:
getchar是获取一个字符,为什么要使用 int 类型的变量来接收这个字符呢?
两个原因:
- getchar返回的是一个字符,而字符在内存中的存储是以ASCII码值的形式存储的,比如小写a在内存中存放的是97(a的ASCII码值为97),97是一个整数,所以将 getchar 函数的返回值放到一个整型变量中是没有任何问题的。
- getchar函数读取失败的时候返回EOF(-1),而 -1 是一个整数占4个字节的空间,char 类型是存放字符的(一个字符只有1个字节的空间)。如果将 -1 放到char类型变量中是放不下的,所以综合考虑将 getchar函数 的返回值放到 int 类型的变量中。
#include<stdio.h>
int main()
{int ch = 0;while ((ch = getchar()) != EOF)putchar(ch);return 0;
}
运行结果:
代码分析:
getchar一次只能读取一个字符,但是在while循环中可以读取多个字符。
putchar一次只能输出一个字符,但是在while循环中可以打印多个字符。
- -1指的是getchar读取字符失败返回的值,但是当你输入 -1的时候,getchar读取-1并没有失败,所以输入-1循环不会终止。
- 如果输入EOF,getchar每次只能获取一个字符,虽然输入了三个字符,但每次的返回值都是一个字符的ASCII码值,会返回3次。这3次中的getchar读取字符都没有失败,所以不会返回-1,所以循环不会终止。
- 当输入Ctrl+z时,getchar读取失败然后返回-1,所以才会终止循环。
输出可以使用 printf 函数,如果想要输出字符也可以使用putchar函数(putchar函数只能用来输出字符)。
putcahr - 输出一个字符:将获取到的字符输出到屏幕上。
putchar 函数的使用
#include<stdio.h>
int main()
{int ch = 0;//scanf("%d",&ch);//输入a的ASCII码值(97)ch = getchar();//printf("%c\n", ch);//%c - 打印字符putchar(ch);return 0;
}
运行结果
#include<stdio.h>
int main()
{char input[20] = { 0 };printf("请输入密码:>");scanf("%s", input);printf("请确认密码(Y/N):>");int ch = getchar();if ('Y' == ch){printf("确认成功\n");}else{printf("确认失败\n");}return 0;
}
运行结果:
程序的本意是:输入密码,再输入Y 或 N 确认密码,输入Y屏幕输出确认成功,输入N屏幕输出确认失。可是当输完密码后按回车键,程序直接输出确认失败。
程序执行的结果偏离了我们的本意,为什么会这样呢?
原因:
- 程序运行以后,在屏幕上输出:请输入密码:> 这个时候程序在等待着我们去输入密码。而scanf 读取的数据是从键盘中输入的数据。
- scanf 在读取数据的时候并不是直接从键盘中获取数据,从键盘输入数据的时候有一个输入缓冲区。当scanf 在工作的时候会到输入缓冲区中获取数据(如果输入缓冲区中有数据,scanf就会直接拿走缓冲区的数据;如果输入缓冲区中没有数据,scanf 就会一直等待键盘输入数据,然后再读取数据)。
- 程序中输入密码:abcdef,为了让scanf获取到密码(abcdef)还要在键盘上敲回车键(相当于\n),敲回车是为了让input获取密码(abcdef)。此时输入缓冲区中存放的数据为:abcdef\n,当敲完回车键后 abcdef 就被scanf拿走。
- abcdef被拿走后输入缓冲区还剩下一个字符 '\n',此时程序会输出:请确认密码(Y/N):> 。然后getchar开始工作,getchar也是从输入缓冲区中获取数据(如果缓冲区中有数据getchar就会直接拿走数据,如果缓冲区没有数据getchar就会等待键盘输入数据,然后再读取数据)。
- 此时缓冲区里还有一个字符'\n',getchar就会直接拿走'\n' 将'\n'放到变量ch里。此时ch再进行判断:ch不等于Y,程序就走到else语句中,然后输出"确认失败"。
- 得出:getchar在拿取缓冲区的数据时,因为缓冲区里有数据('\n')所以程序不会等待着让我们输入 (Y 或 N ),如果缓冲区中没有数据,那么getchar就会等待键盘输入信息放到缓冲区里,然后再从缓冲区中读取数据,再进行判断。
解决办法:在scanf后面再写一个getchar,让第一个getchar先将缓冲区中的'\n'拿走(此时的getchar不需要返回值),再执行第二个getchar的时候缓冲区里就没有数据了,这个时候程序就会等待我们输入数据。
修改后的代码:
#include<stdio.h>
int main()
{char input[20] = { 0 };printf("请输入密码:>");scanf("%s", input);getchar();//拿走'\n'printf("请确认密码(Y/N):>");int ch = getchar();if ('Y' == ch){printf("确认成功\n");}else{printf("确认失败\n");}return 0;
}
运行结果:
但是此时程序还是有问题,如果有人在输入密码的时候中间加了空格,程序执行的结果依旧不是我们想要的结果:
补充:scanf("%s",....); 读取字符串的时候,遇到空格或换行('\n')就结束了。如果在字符串中想要读取空格可以使用gets函数。这里只是提一下gets函数,后期会详细说这个函数。
代码分析:
- scanf在输入缓冲区中读取数据时,当读取到abcdef后面的空格时就不会再读取了,此时缓冲区中还剩下:空格abc\n。
- 程序往下执行到第一个getchar时只能拿走缓冲区里的空格字符,拿走以后缓冲区中还有:abc\n。
- 程序再往下执行到第二个getchar时发现缓冲区中有数据就直接将a拿走,a不等于 Y 。所以程序输出:确认失败。
解决办法:
使第一个 getchar 将缓冲区里的所有数据都拿走(最后一个数据是'\n')。此时就要使用循环来读取字符,直到将'\n'也拿去。
修改后的代码:
#include<stdio.h>
int main()
{char input[20] = { 0 };printf("请输入密码:>");scanf("%s", input);int tmp = 0;//清理缓冲区while ((tmp = getchar()) != '\n'); //只循环//也可以写成//while ((tmp = getchar()) != '\n')//{// ;//空语句//}printf("请确认密码(Y/N):>");int ch = getchar();if ('Y' == ch){printf("确认成功\n");}else{printf("确认失败\n");}return 0;
}
运行结果:
scanf 函数的功能比getchar函数的功能更全, printf 函数的功能比putchar函数的功能更全。
getchar 和 putchar 只能操作字符(一次只能操作一个字符)。
总结
详细介绍了C语言中的循环语句,可能有些地方写的不是很清晰(请谅解)。
还介绍了getchar函数与putchar函数。getchar与putchar是使用文字描述的,可能会太抽象,不容易理解。
学习是循序渐进的过程,每天学一点就会有收获!
C语言初级篇-----循环语句详解相关推荐
- c语言输入循环语句,C语言中输入输出和循环语句详解.ppt
C语言中输入输出和循环语句详解 C语言的输入输和循环分支语句详解 本章主要向大家介绍下C语言的输入和输出功能以及循环分支语句的使用 第4章 顺序结构程序设计 3.1 C语言语句 3.2 赋值语句 3. ...
- smarty手册-smarty中foreach循环语句详解
原文地址:smarty手册-smarty中foreach循环语句详解作者:谭博 {foreach}循环也有自身属性的变量,可以通过{$smarty.foreach.name.property}访问,其 ...
- C语言入门篇 | 循环语句和用法(详解版)
C语言循环语句用法有三种主要形式: for()循环 while()循环 do {} while();循环 for()循环用法: for( e1 ; e2 ; e3 ) //注意三个区域间用分号来分隔, ...
- C语言详解系列——循环语句详解(1)while语句的语法结构
文章目录 while语句 break continue while语句 之前的学习中我们了解到了if语句的用法,这个语句只会执行一次,但在我们的生活当中有许多事情是需要重复去做的,那我们应该怎么实现呢 ...
- C语言详解系列——循环语句详解(3)do while语句的语法结构
文章目录 do while语句 do while语句中的break和continue break continue do while语句与while语句的不同点 简单练习 do while语句 之前的 ...
- C语言详解系列——循环语句详解(2)for语句的语法结构
文章目录 for语句 break与continue 关于if语句的建议 关于for循环的变种写法 一道笔试题 for语句 在之前的学习中我们已经了解到了循环结构中while语句的用法,那么为什么我们还 ...
- python循环语句-详解Python中的循环语句的用法
一.简介 Python的条件和循环语句,决定了程序的控制流程,体现结构的多样性.须重要理解,if.while.for以及与它们相搭配的 else. elif.break.continue和pass语句 ...
- matlab循环语句详解
MATLAB中的循环语句是一种方便的编程结构,可以帮助处理迭代过程中的数据和控制流.MATLAB提供了三种基本类型的循环语句:for循环.while循环和do-while循环.这篇文章将介绍这些循环语 ...
- PHP循环语句详解 while, for, foreach, do while
一.while循环 while(表达式) { 循环体;//反复执行,直到表达式为假 } 代码: $index = 1; while ($index<5) { print "Numbe ...
最新文章
- JDBC Driver常用连接方法列表
- 如何用java+tomcat来创建网站服务器
- 如何使用 Istio 进行多集群部署管理:多控制平面
- 维护没有源代码的遗留 Java 项目
- CSV格式数据如何导入MySQL?
- C及opencv指针释放问题
- C++中引用与指针的区别(详细介绍)
- mysql 非自然月统计_MySQL性能优化 — 实践篇1
- 关于npm邮箱验证问题
- git pull 出错,error: Your local changes to the following files would be overwritten by merge...
- 2.4 sklearn中的metrics.roc_auc_score评价指标
- Centos7安装32位库用来安装32位软件程序
- 为什么局域网需要https加密?如何选择内网SSL证书?
- java画乌龟_简单的实现java多线程——龟兔赛跑
- 【jzoj1592】【二分】【GDKOI训练】音乐节拍
- mysql拼图游戏代码_HTML+Javascript制作拼图小游戏详解(二)
- 含有中文的图片正常显示(微信小程序)
- Batch Normalization在CNN中的原理,nb与lrb的区别
- IOS - 怎么辨别苹果手机的数据线是否原装?
- 写论文比较有用的网站
热门文章
- G1D19-DPCS KEAttacKG复现
- ceph-mimic版本的安装使用1
- Spring 源码中设计模式?这样回答面试官.才能显得你技高一筹.
- cocoscreator 动态创建帧动画
- 电脑高手常用的五个按钮
- springboot处理参数再转发请求_Springboot 2.0---WebFlux请求处理流程
- 移动端UI框架小汇总
- STM32的USART_GetFlagStatus和USART_GetITStatus解析
- Echarts --- 可视化练习(pie01 --- 南丁格尔玫瑰图)
- 远程桌面连接时,用电脑名不能连接,用IP就可以,为什么