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~100
	int 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语句的循环控制变量

建议:

  1.  不可在for 循环体内修改循环变量,防止 for 循环失去控制。
  2.  建议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;
	//代码1
	printf("代码1:\n");
	for (i = 0; i < 10; i++)
	{
		printf("hello\n");
	}
	printf("-------------------\n");
	//代码2
	printf("代码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;
}
  1. for循环的初始化(表达式1)、判断(表达式2)、调整(表达式3)三个部分可以省略。
  2. 判断部分(表达式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的阶乘

  1. 输入 n
  2. 产生1~n的数字,累积乘在一起
  3. 打印出来
#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: ############

  1.  H##########!
  2.  He########d!
  3.  Hel######ld!
  4.        ……
  5.  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 类型的变量来接收这个字符呢?

两个原因:

  1. getchar返回的是一个字符,而字符在内存中的存储是以ASCII码值的形式存储的,比如小写a在内存中存放的是97(a的ASCII码值为97),97是一个整数,所以将 getchar 函数的返回值放到一个整型变量中是没有任何问题的。
  2. 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是使用文字描述的,可能会太抽象,不容易理解。

学习是循序渐进的过程,每天学一点就会有收获!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇湘夜雨.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值