一、while循环
C语言提供了3种循环语句, while 就是其中⼀种,接下来就介绍⼀下 while 语句。
while 语句的语法结构和 if 语句非常相似——默认只跟一条语句,有多条语句需要使用大括号括起来。
1.1 if和while的对比
if(表达式)
语句;
while(表达式)
语句;//如果循环体想包含更多的语句,可以加上⼤括号
具体来看:
//代码1
#include <stdio.h>
int main()
{
if(1)
printf("hehe\n"); //if后边条件满⾜,打印⼀次hehe
return 0;
}
//代码2
#include <stdio.h>
int main()
{
while(1)
printf("hehe\n"); //while后边的条件满⾜,死循环的打印hehe
return 0;
}
这就是他们的区别,while语句是可以实现循环效果的。
1.2 while语句的执行流程
N-S图:结构化编程中的一种可视化建模。
首先上来就是执行判断表达式,表达式的值为0,循环直接结束;表达式的值不为0,则执行循环语 句,语句执行完后再继续判断,是否进行下一次判断。
1.3 while循环的实践
练习:在屏幕上打印1~10的值
参考代码:
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
printf("%d ", i);
i = i+1;
}
return 0;
}
注意死循环:
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
printf("%d ", i);
i = i+1;
return 0;
}
1.4 练习
题目解析
1. 要想得到n的最低位,可以使用n%10的运算,得到的余数就是最低位,如:1234%10得到4
2. 要想去掉n的最低位,找出倒数第二位,则使用 n=n/10 操作就可以去掉最低位的,如:
n=1234/10得到123,123相较于1234就去掉了最低位,123%10就得到倒数第⼆位3。
3. 循环1和2两个步骤,在n变成0之前,就能到所有的位。
参考代码:
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
while(n)
{
printf("%d ", n%10);
n /= 10;
}
return 0;
}
注意:整型输入的时候不能越界!!!
二、for循环
观察之前的while循环,发现其有3个关乎循环情况的部分:
这三个部分对于一个循环来说是至关重要的,任何一个部分改变,循环的情况都会发生对应的改变,而对于while循环来说,这三个部分是相对分散的,如过这个代码比较复杂,循环的体量比较大,三个部分之间又挤入了大量其他的执行代码,这个时候你会发现初始化、判断、调整这三个部分离得越来越远,导致不便于观察循环的结构、不利于对循环的维护。代码容易失去控制。
2.1 语法形式
for 循环是三种循环中使用最多的, for 循环的语法形式如下:
for(表达式1; 表达式2; 表达式3)
语句;//如果循环体想包含更多的语句,可以加上⼤括号
表达式1 用于循环变量的初始化
表达式2 用于循环结束条件的判断
表达式3 用于循环变量的调整
for循环的初始化、判断、循环是哪个表达式在语法上都可以省略。
但是判断部分省略,要注意:如果省略了表示判断部分恒为真。
for循环的初始化部分、调整部分都可以省略,判断部分如果省略就会变成一个死循环——依靠break跳出循环——类似于while(1)
示例1:
int main()
{
int i = 0;
int j = 0;
for(i = 0; i < 5; i++)
{
for(j = 0; j < 5; j++)
{
printf("hehe\n");
}
}
return 0;
}
结果1:打印25个hehe。
示例2:
int main()
{
int i = 0;
int j = 0;
for(; i < 5; i++)
{
for(; j < 5; j++)
{
printf("hehe\n");
}
}
return 0;
}
结果2:打印5个hehe。
注:C99标准
1. C99及以后标准:从C99标准开始,允许在`for`循环的初始化部分直接声明变量。因此,以下两种写法都是合法的:
int i = 0;
for (i = 0; i < 10; i++) {
// 循环体
}
```
```
for (int i = 0; i < 10; i++) {
// 循环体
}
```
在C99及以后标准中,`for (int i = 0; ...)`是完全合法的,并且变量`i`的作用域仅限于`for`循环内部,这有助于减少变量的生命周期,避免在循环外意外使用该变量。
2. C90标准:在C90标准中,变量必须在代码块的开头声明,因此`for (int i = 0; ...)`是不合法的。如果使用C90标准,只能写成:
```
int i;
for (i = 0; i < 10; i++) {
// 循环体
}
```
3. 编译器设置:如果你使用的是现代编译器(如GCC、Clang、VS等),默认情况下它们通常支持C99或更高标准,因此`for (int i = 0; ...)`是可以使用的。但如果你将编译器设置为严格遵循C90标准(例如GCC中使用`-std=c89`选项),那么这种写法会报错。
总结
如果你使用的是现代C语言标准(C99或更高),`for (int i = 0; ...)`是完全可以使用的,并且是一种更推荐的写法,因为它可以更好地控制变量的作用域。如果你使用的是C90标准,那么只能在循环外声明变量。
2.2 for循环的执行流程
首先执行 表达式1 初始化循环变量——只执行一次,接下来就是执行 表达式2 的判断部分, 表达式2 的结果如果 ==0,则循环结束; 表达式2 的结果如果!=0则执行循环语句,循环语句执行完后,再去执行表 达式3 ,调整循环变量,然后再去 表达式2 的地方执行判断, 表达式2 的结果是否为0,决定循环是否继续。
整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环。
2.3 for循环的实践
练习:在屏幕上打印1~10的值
参考代码:
#include <stdio.h>
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
printf("%d ", i);
}
return 0;
}
运行结果:
2.4 while循环和for循环的对比
2.5 练习
练习1: 计算1~100之间3的倍数的数字之和
思路1:产生1~100的数——>找出3的倍数——>求和。
思路2:产生1~100之间3的倍数——>求和。
参考代码:
#include <stdio.h>
int main()
{
int i = 0;
int sum = 0;
for(i=1; i<=100; i++)
{
if(i % 3 == 0)
sum += i;
}
printf("%d\n", sum);
return 0;
}
//⼩⼩的优化
//如果能直接产⽣3的倍数的数字就省去了多余的循环和判断
#include <stdio.h>
int main()
{
int i = 0;
int sum = 0;
for(i=3; i<=100; i+=3)
{
sum += i;
}
printf("%d\n", sum);
return 0;
}
2.6 for循环内声明的变量的生命周期和作用域问题
如果变量`int a = 0`是在循环体的大括号内声明的,那么它会在每次循环迭代时重新执行,并且每次迭代都会重新初始化为`0`。
示例代码:
```
for (int i = 0; i < 3; i++) {
int a = 0; // 在循环体内部声明
a++;
printf("i = %d, a = %d\n", i, a);
}
```
输出结果:
```
i = 0, a = 1
i = 1, a = 1
i = 2, a = 1
```
解释
1. 变量的作用域:在循环体内部声明的变量`a`的作用域仅限于当前循环体的大括号`{}`内。
2. 每次迭代重新初始化:
• 每次进入循环体时,`int a = 0`会被重新执行,`a`会被重新初始化为`0`。
• 然后执行`a++`,因此每次打印的`a`都是`1`。
如果变量在循环体外声明
如果变量`a`在循环体外声明,那么它只会初始化一次,并且会在每次循环迭代中保持其值。
```
int a = 0;
for (int i = 0; i < 3; i++) {
a++;
printf("i = %d, a = %d\n", i, a);
}
```
输出结果
```
i = 0, a = 1
i = 1, a = 2
i = 2, a = 3
```
总结
• 在循环体内部声明变量:每次循环迭代都会重新初始化。
• 在循环体外部声明变量:只会初始化一次,后续迭代会保持其值。
在循环体内部声明的变量的生命周期仅限于当前循环迭代。这意味着每次进入循环体时,变量会被重新声明和初始化,当退出当前迭代(即离开大括号`{}`)时,变量的生命周期结束。
说明:
1.生命周期的范围
• 一次循环迭代:在循环体内部声明的变量,其生命周期从当前迭代的开始到当前迭代的结束。
• 每次迭代独立:每次迭代时,变量都会被重新声明和初始化,与上一次迭代无关。
2.示例说明
以下代码展示了变量在循环体内部声明时的行为:
```
for (int i = 0; i < 3; i++) {
int a = 10; // 在循环体内部声明
printf("Iteration %d: a = %d\n", i, a);
a++; // 修改变量
}
```
输出结果:
```
Iteration 0: a = 10
Iteration 1: a = 10
Iteration 2: a = 10
```
解释:
• 每次进入循环体时,`int a = 10`都会被重新执行,`a`被重新初始化为`10`。
• 当离开当前迭代时(即离开大括号`{}`),变量`a`的生命周期结束,下一次迭代时会重新声明和初始化。
• 因此,每次迭代中`a`的值都是独立的,不会保留上一次迭代的值。
3.与循环体外声明变量的对比
如果变量在循环体外声明,其生命周期会覆盖整个循环过程:
```
int a = 10;
for (int i = 0; i < 3; i++) {
printf("Iteration %d: a = %d\n", i, a);
a++; // 修改变量
}
```
输出结果:
```
Iteration 0: a = 10
Iteration 1: a = 11
Iteration 2: a = 12
```
解释:
• 变量`a`在循环体外声明,因此它只初始化一次。
• 每次迭代中,`a`的值会保留上一次迭代的修改。
总结
• 在循环体内部声明的变量:生命周期仅限于当前循环迭代,每次迭代独立。
• 在循环体外部声明的变量:生命周期覆盖整个循环过程,值会在每次迭代中保留。
这种行为使得在循环体内部声明的变量非常适合用于局部作用域的变量,避免了变量在循环外被意外访问或修改。
三、do-while循环
3.1 语法形式
在循环语句中 do while 语句的使用最少,它的语法如下:
do
语句;
while(表达式);
do也是默认只跟一条语句,跟多条语句要使用大括号括起来。
while 和 for 这两种循环都是先判断,条件如果满足就进入循环,执行循环语句,如果不满足就跳 出循环;
而 do while 循环则是先直接进入循环体,执行循环语句,然后再执行 while 后的判断表达式,表 达式为真,就会进行下⼀次,表达式为假,则不再继续循环。
3.2 do-while循环的执行流程
在 do while 循环中先执行图上的“语句”,执行完语句,在去执行“判断表达式”,判断表达式的结果是!=0,则继续循环,执行循环语句;判断表达式的结果==0,则循环结束。所以在 do while 语句中循环体是至少执行一次的,这是 do while 循环比较特殊的地方。
for循环和while循环都是先判断,再执行,do-while循环是先执行再判断。
3.3 do-while循环的实例
练习:在屏幕上打印1~10的值
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("%d ", i);
i = i + 1;
}while(i<=10);
return 0;
}
一般 do while 使用在循环体至少被执行⼀次的场景下,所以较少一些。
3.4 练习
参考代码:
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
//int cnt = 1;
//do
//{
// n /= 10;
// if(n != 0)
// cnt++;
// else
// break;
//} while (1);
int cnt = 0;
do
{
cnt++;
n = n / 10;
} while (n);
printf("%d\n", cnt);
return 0;
}
这里并非必须使用 do while 语句,但是这个代码就比较适合使用 do while 循环,因为n即使是
0,也是1位数,要统计位数的。
四、循环的嵌套
前面学习了三种循环 while , do while , for ,这三种循环往往会嵌套在一起才能更好的解决问题,就是我们所说的:循环嵌套,这里我们就看⼀个例子。
4.1 练习
找出100~200之间的素数,并打印在屏幕上。 (注:素数又称质数,只能被1和本身整除的数字)
4.2 分析
1. 要从100~200之间找出素数,首先得产生100~200之间的数,这里可以使用循环解决。
(注:循环的用途——1.产生a~b之间的数字—>2.执行一定次数的某个工作(例如打印图案)—>3.遍历顺序表、链表)
2. 假设要判断i是否为素数,需要拿2~i-1之间的数字去试除i,需要产生2~i-1之间的数字,也可以使用循环解决。
3. 如果2~i-1之间有数字能整除i——模为0,则i不是素数,如果都不能整除,则i是素数。
4.3 参考代码
#include <stdio.h>
int main()
{
int i = 0;
//循环产⽣100~200的数字
for(i=100; i<=200; i++)
{
//判断i是否为素数
//循环产⽣2~i-1之间的数字
int j = 0;
int flag = 1;//假设i是素数
for(j=2; j<i; j++)
{
if(i % j == 0)
{
flag = 0;
break; //加上break表示一旦出现整除,就不再继续往后除了
}
}
if(flag == 1)
printf("%d ", i);
}
return 0;
}
加上计数器变量就能统计质数的个数。
代码优化1:只判断奇数,减少一半的数字判断。
#include <stdio.h>
int main()
{
int i = 0;
//循环产⽣100~200的数字
for(i=101; i<=200; i+=2) //除2外偶数不可能是质数,产生的所有都是奇数,减少一半的数字判断
{
//判断i是否为素数
//循环产⽣2~i-1之间的数字
int j = 0;
int flag = 1;//假设i是素数
for(j=2; j<i; j++)
{
if(i % j == 0)
{
flag = 0;
break; //加上break表示一旦出现整除,就不再继续往后除了
}
}
if(flag == 1)
printf("%d ", i);
}
return 0;
}
又有:一个合数n的两个因子必有一个小于等于根号n——能在根号n后找到一个因子,则在根号n之前也必有一个因子。
代码优化2:
#include <stdio.h>
#include <math.h>
int main()
{
int i = 0;
//循环产⽣100~200的数字
for(i=101; i<=200; i+=2) //除2外偶数不可能是质数,产生的所有都是奇数,减少一半的数字判断
{
//判断i是否为素数
//循环产⽣2~i-1之间的数字
int j = 0;
int flag = 1;//假设i是素数
for(j=2; j<= sqrt(i); j++) //sqrt 开平方 库函数
{
if(i % j == 0)
{
flag = 0;
break; //加上break表示一旦出现整除,就不再继续往后除了
}
}
if(flag == 1)
printf("%d ", i);
}
return 0;
}