【C语言】循环语句详解

✨个人主页: Anmia.
🎉所属专栏: C Language

🎃操作环境: Visual Studio 2019 版本

目录

1.什么是循环结构?

2.while循环

while流程图

while语句中的break和continue

break

continue

3.for循环

for流程图

for循环中的break和continue

break

continue

for语句的循环控制变量

一些for循环的变种

do...while()循环

do...while()循环流程图

do...while对比while,for有什么特殊?

[do while循环中的break和continue](#do while循环中的break和continue)

break

continue

练习

练习1

练习2

练习3

练习4

练习5

练习6

后言​​​​​​​


1.什么是循环结构?

用顺序结构和选择结构可以解决简单的,不重复的问题,但在日常生活中或者是在应用程序中,许多问题是需要反复处理执行的。

  • 问题1:分别统计全班50个学生的平均成绩。
  • 问题2:求30个整数的和。

要处理以上问题,最原始的方法是分别编写若干个相同或者相似的语句或者程序段来进行处理,这种方法工作量大,程序长,重复,难以阅读和维护。

循环结构又称为重复结构,利用循环结构可以方便的处理这种需要重复处理的问题。大多数应用程序都是包含循环结构。

C语言给我们提供了三种循环语句:

  • while
  • do...while
  • for

2.while循环

我们已经掌握了,if语句:

cpp 复制代码
if(条件)
  语句;

当条件满足的情况下,if语句后的语句执行,否则不执行。

但是这个语句只会执行一次。

由于我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次。

那我们怎么做呢?

C语言中给我们引入了: while 语句,可以实现循环。

cpp 复制代码
while(表达式)
循环语句;

while流程图


比如我们实现:
在屏幕上打印1-10的数字。

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 1;
    while(i<=10)
    {
        printf("%d ", i);
        i = i+1;
    }
    return 0;
}

以 i 作为是否执行while循环的条件,我们此时i =1 满足 i<=10 这个循环条件,所以加入whilex循环。先打印1,然后i+1变成2,2再和循环条件i<=10进行判断,满足则继续执行循环体,直到i=11不满足i<=10,退出循环体,结束循环。运行结果如下


上面的代码已经帮我了解了 while 语句的基本语法,那我们再了解一下:


while语句中的break和continue

break

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 1;
    while(i<=10)
    {
        if(i == 5)
        break;
        printf("%d ", i);
        i = i+1;
    }
    return 0;
}

这里的输出结果是什么?
答案如图。

看运行结果不难看出,在i 加到5时,在进行if循环判断时,if条件成立,执行break语句。直接跳出了当前的while循环,不再执行后面的循环语句。

通过之前学习的switch中的break,似乎效果一致,即跳出当前的循环语句/switch语句。

  • 总结:
  • break在while循环中的作用:
  • 其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
  • 所以:while中的break是用于永久终止循环的。

continue

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 1;
    while(i<=10)
    {
        i = i+1;
        if(i == 5)
        continue;
        printf("%d ", i);
    }
    return 0;
}

这段代码的运行结果又是什么呢?
答案如图。

看运行结果不难看出,进入循环i先+1后打印,所以没有打印1。当i+1=5时进入if中的语句,执行continue,我们发现没打印5,但是打印了后面的6 7 8 9 10 11。

  • 没打印5的原因:因为continue是结束本次循环后面的语句,因此在Continue执行后,后面的printf被跳过了,直接跳回到i=i+1,继续循环往复。
  • 打印了11的原因:因为i=10仍然是满足条件i<=10后还会执行循环体。
  • 总结:
  • continue在while循环中的作用就是:
  • continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,
  • 而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
    下面再看两段代码,分别是什么意思?
cpp 复制代码
#include <stdio.h>
int main()
{
    int ch = 0;
    while ((ch = getchar()) != EOF)
       putchar(ch);
    return 0;
}

getchar() - 获取(输入)一个字符
getchar 读取字符成功时,返回字符的ASCLL码值
读取失败时 或者 遇到文件末尾时就返回EOF

windows环境Ctrl + z getchar返回 EOF

详细见:

【C语言】EOF搭配while循环来控制scanf输入的次数_Anmia.的博客-CSDN博客

cpp 复制代码
#include <stdio.h>
int main()
{
    char ch = '\0';
    while ((ch = getchar()) != EOF)
    {
      if (ch < '0' || ch > '9')
          continue;
      putchar(ch);
    }
    return 0;
}

这个代码的作用是:只打印数字字符,跳过其他字符的。


3.for循环

我们已经知道了while循环,但是我们为什么还要一个for循环呢?

首先来看看for循环的语法:

cpp 复制代码
for(表达式1; 表达式2; 表达式3)
    循环语句;
  • 表达式1:为初始化部分,用于初始化循环变量的。
  • 表达式2:为条件判断部分,用于判断循环时候终止。
  • 表达式3:为调整部分,用于循环条件的调整。
    例子1:

使用for循环 在屏幕上打印1-10的数字。

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 0;
    //for(i=1/*初始化*/; i<=10/*判断部分*/; i++/*调整部分*/)
    for(i=1; i<=10; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

for流程图

现在我们对比一下for循环和while循环。

cpp 复制代码
//实现相同的功能,使用while
int i = 0;
i=1;//初始化部分
while(i<=10)//判断部分
{
    printf("hehe\n");
    i = i+1;//调整部分
}

//实现相同的功能,使用for
for(i=1; i<=10; i++)
{
    printf("hehe\n");
}

可以发现在while循环中依然存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较

远,这样

查找修改就不够集中和方便。所以,for循环的风格更胜一筹;for循环使用的频率也最高。


for循环中的break和continue

break

我们发现在for循环中也可以出现break和continue,他们的意义和在while循环中是一样的。

但是还是有些差异:

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 0;
    for(i=1; i<=10; i++)
    {
        if(i == 5)
            break;
        printf("%d ",i);
    }
    return 0;
}

continue

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 0;
    for(i=1; i<=10; i++)
    {
        if(i == 5)
            continue;
        printf("%d ",i);
    }
    return 0;
}

通过和while的代码对比,相信不难区分它们的写法


for语句的循环控制变量

建议:

  1. 不可在for 循环体内修改循环变量,防止 for 循环失去控制。

  2. 建议for语句的循环控制变量的取值采用"前闭后开区间"写法。

cpp 复制代码
int i = 0;
//前闭后开的写法
for(i=0; i<10; i++)
{}

//两边都是闭区间
for(i=0; i<=9; i++)
{}

一些for循环的变种

cpp 复制代码
#include <stdio.h>
int main()
{
	for (;;)
	{
		printf("hehe\n");
	}
}

for循环中的初始化部分,判断部分,调整部分是可以省略的,但是不建议初学时省略,容易导致问题。


这里打印多少个hehe?

cpp 复制代码
#include <stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 10; i++)
	{
		for (j = 0; j < 10; j++)
		{
			printf("hehe\n");
		}
	}
	return 0;
}

答案是100个,这种形式是for的嵌套。

代码逻辑:

i=0--> i<10成立进入外层for --> j=0 --> j<10成立进入内层for -->打印hehe\n --> j++后 j=2 --> j<10成立继续循环........直到 j=10,j<10不成立跳出内层for---> i++后i=2 --> i<10成立再次进入外层for......直到i=10,j<10不成立跳出外层for,for嵌套结束。

  • 简单点看

无非就是满足for循环条件就进入循环,只是外层for循环体中又有一个内层for而已。


如果省略掉初始化部分,这里打印多少个hehe?

cpp 复制代码
#include <stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	for (; i < 10; i++)
	{
		for (; j < 10; j++)
		{
			printf("hehe\n");
		}
	}
    return 0;
}

​

答案是10个,对比上一题,为什么少打印了那么多?主要问题出现在内层初始化部分被省略了,当j++第一次到j=10内层条件不满足,跳出内层循环到i++,i=2时i<10是满足的,会执行外层for的循环体,可是问题就是j之前已经++到10了,内层for有没有初始化部分,会直接检查条件部分是否满足,但j此时是10了,就j<10这个条件就不满足了,因此不会再执行内层for循环。


使用多余一个变量控制循环

cpp 复制代码
int x, y;
  for (x = 0, y = 0; x<2 && y<5; ++x, y++)
 {
    printf("hehe\n");
 }

例题1:

请问下面循环要循环多少次?

cpp 复制代码
#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 中,应该使用相等运算符 == 而不是赋值运算符 =。


do...while()循环

  • 语法如下:
cpp 复制代码
do
{
 循环语句;
}while(表达式);

do...while()循环流程图


do...while对比while,for有什么特殊?

循环至少执行一次,使用的场景有限,所以不是经常使用。以下是代码案例。

cpp 复制代码
#include <stdio.h>
int main()
{
    int i = 10;
    do
    {
        printf("%d\n", i);
    }while(i<10);
    return 0;
}

do while循环中的break和continue

break

cpp 复制代码
#include <stdio.h>
int main()
{
	int i = 10;

	do
	{
		if (5 == i)
			break;
		printf("%d\n", i);
	} while (i < 10);

	return 0;
}

以上代码,单看i=10肯定是不满足i<10这个循环条件的,但由于do...while循环的特性,它不论循环条件是否成立,都会先执行一次循环体再进行判断。所以上面代码结果是只输出了10。


continue

那这段代码的结果呢?

cpp 复制代码
#include <stdio.h>
int main()
{
	int i = 10;

	do
	{
		if (5 == i)
			continue;
		printf("%d\n", i);
	} while (i < 10);

	return 0;
}

答案也是只输出一个10,原因同上。


练习

练习1

计算 n的阶乘。

cpp 复制代码
#include <stdio.h>

int main() {
    int n;
    unsigned long long factorial = 1;

    printf("请输入一个非负整数:");
    scanf("%d", &n);

    if (n < 0) 
    {
        printf("输入的数必须是非负整数。\n");
    }
    else 
    {
        for (int i = 1; i <= n; i++) {
            factorial *= i;
        }
        printf("%d 的阶乘是 %llu\n", n, factorial);
    }
    return 0;
}

练习2

计算n的阶乘和。

cpp 复制代码
#include <stdio.h>

int main() {
    int n;
    unsigned long long factorialSum = 0;

    printf("请输入一个非负整数:");
    scanf("%d", &n);

    if (n < 0) 
    {
        printf("输入的数必须是非负整数。\n");
    }
    else {
        for (int i = 1; i <= n; ++i) {
            unsigned long long factorial = 1;
            for (int j = 1; j <= i; ++j) {
                factorial *= j;
            }
            factorialSum += factorial;
        }
        printf("从1到%d的阶乘和是 %llu\n", n, factorialSum);
    }
    return 0;
}

这段代码首先要求用户输入一个非负整数,并将其存储在变量 n 中。然后,使用两个嵌套的 for 循环来计算从 1 到 n 的所有整数的阶乘,并将每个阶乘的结果累加到 factorialSum 变量中。最后,使用 printf 函数将结果输出到屏幕上。


练习3

用二分查找在一个有序数组中查找具体的某个数字n

cpp 复制代码
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int left = 0;//左下标
	int right = sz - 1;//右下标
	int num = 0;
	int flag = 0;
	scanf("%d", &num);
	while (left <= right)
	{
		int mid = (left + right) / 2;//(左下标+右下标)/2
		if (arr[mid] < num)
		{
			left = mid + 1;
		}
		else if (arr[mid] > num)
		{
			right = mid - 1;
		}
		else
		{
			printf("找到了,数字%d的下标是:%d\n", num, mid);
			flag = 1;
			break;
		}
	}
	if (flag == 0)
	{
		printf("没找到!");
	}
	return 0;
}

这段代码使用二分查找算法在给定的有序数组中查找一个特定的数字。

首先,定义一个整型数组 arr,并初始化为 {1,2,3,4,5,6,7,8,9,10}。然后,计算数组的大小 sz,即数组中元素的个数。

接下来,定义两个变量 leftright 分别表示数组的左下标和右下标。初始时,left 设为 0,right 设为 sz - 1

然后,通过用户输入获取一个数字 num

接下来,使用一个循环来执行二分查找。循环条件是 left <= right,即左下标小于等于右下标。在每次循环中,计算中间下标 mid,即 (left + right) / 2

然后,根据中间元素的值与目标数字 num 的大小关系,更新左下标和右下标。如果中间元素小于 num,则将左下标 left 更新为 mid + 1;如果中间元素大于 num,则将右下标 right 更新为 mid - 1

如果中间元素等于 num,则找到了目标数字,输出找到的消息,并将标志变量 flag 设为 1,然后使用 break 语句跳出循环。

如果循环结束后 flag 仍为 0,表示未找到目标数字,输出未找到的消息。

最后,返回 0 表示程序执行完毕。

这段代码适用于在有序数组中查找特定数字的场景。如果数组是无序的,二分查找算法将不适用,需要使用其他查找算法。


练习4

编写代码,演示多个字符从两端移动,向中间汇聚

cpp 复制代码
#include <string.h>
#include <windows.h>
int main()
{
	char arr1[] = "Hello World!!!";
	char arr2[] = "**************";
	int left = 0;//左下标
	int right = strlen(arr2) - 1;//右下标//-1是因为数组下标从0开始
	
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000);//等待一秒
		system("cls");//清空屏幕
		left++;
		right--;
	}
	printf("%s\n", arr2);
	printf("%d\n", strlen(arr2));
	return 0;
}

这段代码使用两个字符数组 arr1arr2 来模拟一个动态变化的字符串效果。

首先,定义一个字符数组 arr1 并初始化为 "Hello World!!!",定义另一个字符数组 arr2 并初始化为 "**************",长度与 arr1 相同。

接下来,定义两个变量 leftright 分别表示 arr2 的左下标和右下标。初始时,left 设为 0,right 设为 strlen(arr2) - 1,其中 strlen(arr2) 函数用于获取 arr2 的长度。

然后,使用一个循环来依次将 arr1 中的字符赋值给 arr2 对应位置的字符,并在每次循环中输出 arr2 的内容。为了实现动态效果,使用 Sleep 函数暂停一秒钟,然后使用 system("cls") 函数清空屏幕。

循环结束后,输出最终的 arr2 的内容和长度。

最后,返回 0 表示程序执行完毕。

这段代码的目的是实现一个字符动画效果,将 arr1 中的字符逐渐替换 arr2 中的字符,并在每次替换后输出 arr2 的内容。通过循环中的延时和清空屏幕操作,实现了字符动态变化的效果。


练习5

编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序。

cpp 复制代码
#include <string.h>
#include <windows.h>
int main()
{
	int i;
	char password[20] = { 0 };
	for (i = 0; i < 3; i++)
	{
		printf("请输入密码:>");
		scanf("%s", password);
		if (strcmp(password, "123456") == 0)
		{
			printf("密码正确!\n");
			break;
		}
		else
		{
			printf("密码错误!\n");
			Sleep(1000);
			system("cls");
		}
	}
	if (i == 3)
	{
		printf("三次输入错误,登录失败!");
	}
	return 0;
}

这段代码实现了一个简单的密码验证功能。

首先,定义一个整型变量 i,用于记录密码输入的次数。

然后,定义一个字符数组 password,长度为 20,并初始化为全零。

接下来,使用一个 for 循环,循环次数为 3,表示最多允许输入密码三次。

在每次循环中,使用 printf 函数提示用户输入密码,并使用 scanf 函数获取用户输入的密码,并将其存储在 password 数组中

然后,使用 strcmp 函数将用户输入的密码与预设的密码 "123456" 进行比较。如果两个密码相等,即返回值为 0,表示密码正确,输出密码正确的消息,并使用 break 语句跳出循环。

如果密码不正确,则输出密码错误的消息,使用 Sleep 函数暂停一秒钟,然后使用 system("cls") 函数清空屏幕,以便下一次输入密码。

循环结束后,判断变量 i 的值是否等于 3。如果等于 3,表示密码输入错误的次数已达到上限,输出登录失败的消息。

最后,返回 0 表示程序执行完毕。

这段代码的目的是实现一个简单的密码验证功能。用户可以最多输入三次密码,如果密码输入正确,则输出密码正确的消息并登录成功;如果密码输入错误,则输出密码错误的消息,并在每次错误后暂停一秒钟并清空屏幕,以便重新输入密码。如果连续三次输入都错误,则输出登录失败的消息。


练习6

猜数字游戏实现

cpp 复制代码
#include <stdlib.h>//rand 和 srand需要
#include <time.h>	//time需要
void menu()
{
	printf("********************\n");
	printf("****  1.  play  ****\n");
	printf("****  0.  exit  ****\n");
	printf("********************\n");
}
void game()
{
	//1. 生成1-100随机数
	//rand函数,生成的是伪随机数,范围是0-RAND_MAX(32767);
	//rand函数需要用srand函数来设置随机数生成器
	
	int ret = rand() % 100 + 1;//%100-->(0~99)   +1 -->(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()
{
	srand((unsigned int)time(NULL));//unsigned int 是因为srand函数的参数需要,所以要强制类型转化
									//NULL是因为time函数参数需要一个指针变量,所以我们可以用空指针代替。
	int input = 0;
	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;
}

这段代码实现了一个猜数字游戏。游戏开始时,会显示一个菜单,让玩家选择是否开始游戏或退出游戏。

game() 函数中,首先使用 rand() 函数生成一个 1 到 100 之间的随机数,并将其赋值给变量 ret

然后,使用一个 while 循环,让玩家不断输入猜测的数字,直到猜对为止。

在每次循环中,玩家输入猜测的数字,并与随机数 ret 进行比较。

如果猜测的数字小于 ret,则输出 "猜小了"。

如果猜测的数字大于 ret,则输出 "猜大了"。

如果猜测的数字等于 ret,则输出 "猜对了",并使用 break 语句跳出循环。

main() 函数中,使用 srand() 函数设置随机数生成器的种子,以确保每次运行程序时生成的随机数不同。

然后,使用一个 do-while 循环,显示菜单并根据玩家的选择执行相应的操作。如果玩家输入的是 1,则调用 game() 函数开始游戏;如果玩家输入的是 0,则输出 "退出游戏" 并结束循环;如果玩家输入的是其他数字,则输出 "输入错误,请重新输入"。

循环条件是 input,当玩家输入 0 时,循环结束。

最后,返回 0 表示程序执行完毕。

这段代码的目的是实现一个简单的猜数字游戏。玩家根据提示输入猜测的数字,通过与随机生成的数字进行比较,判断猜测的数字是大了、小了还是猜对了,并给出相应的提示。玩家可以选择继续游戏或退出游戏。


后言

本篇较为详细的将了循环语句中的重点,以及6道测试题检测学习情况,对后面学习可以起到一定的理解帮助。希望可以得到支持,后面会持续输出各项详解。

相关推荐
Amo Xiang20 分钟前
Django 2024全栈开发指南(一):框架简介、环境搭建与项目结构
python·django
Mr_Xuhhh23 分钟前
递归搜索与回溯算法
c语言·开发语言·c++·算法·github
SoraLuna26 分钟前
「Mac玩转仓颉内测版12」PTA刷题篇3 - L1-003 个位数统计
算法·macos·cangjie
Amo Xiang28 分钟前
Django 2024全栈开发指南(二):Django项目配置详解
python·django
无敌岩雀28 分钟前
C++设计模式行为模式———命令模式
c++·设计模式·命令模式
qq_441996051 小时前
Mybatis官方生成器使用示例
java·mybatis
这个男人是小帅1 小时前
【GAT】 代码详解 (1) 运行方法【pytorch】可运行版本
人工智能·pytorch·python·深度学习·分类
巨大八爪鱼1 小时前
XP系统下用mod_jk 1.2.40整合apache2.2.16和tomcat 6.0.29,让apache可以同时访问php和jsp页面
java·tomcat·apache·mod_jk
爱吃生蚝的于勒2 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
码上一元3 小时前
SpringBoot自动装配原理解析
java·spring boot·后端