前言
顺序、选择、分支是C++程序的三大基本结构,这三种结构的互相嵌套,可以使得我们的代码实现多种多样的功能,所以,对这三种结构的深入了解,是我们学习C++编程的重要一环
顺序
顺序结构是最简单的程序结构,也是最常用的程序结构,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行每一个语句
我们借用上一篇文章末尾的关于取地址符讲解的代码
#include<cstdio>
using namespace std;
int a;
int main()
{
printf("a的数值:%d\n",a);
printf("a的地址(10进制):%d\n",&a);
printf("a的地址(16进制):%x\n",&a);
return 0;
}
这就是一个典型的顺序结构
程序在主函数中,先打印了 变量a 的数值,然后以十进制打印了 变量a 的内存地址,最后以十六进制打印了 变量a 的内存地址
不难看出,代码是以至上而下,逐个语句在运行
分支
顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用分支结构
分支结构的执行是依据一定的条件选择执行路径,而不是严格按照语句出现的物理顺序
如同你玩游戏,不同的条件就可以解锁不同剧情,使得游戏体验增加,在代码中也是这样,合理的使用分支语句,同样可以使我们代码功能更加丰富,以便适应不同需求(适应不同数据来骗分[狗头])
if…else…结构
在分支结构中,if…else…是最基础的实现方式,其基本语法如下:
#include<cstdio>
using namespace std;
int main()
{
int a;
printf("请输入你的IOI分数:");
scanf("%d",&a);
if(a==300)
printf("参见AK大佬");
else
printf("还有提升空间");
return 0;
}
在这个代码中,程序的运行方式如下图
在这里,实现分支的核心语句就是
if(条件)
代码块1;
else
代码块2;
不难看出,在 if 后面我们需要加入一个判定的条件,当这个条件满足时,程序便会执行紧跟 if 的 代码块1 中的内容,如果不满足条件,便会执行紧跟 else 的 代码块2 中的内容
细心的同学肯定会发现,我们的条件是 如果a等于300 ,但是在代码中,我们写入的却是 if ( a == 300 ) 而不是 if ( a = 300 ) ,这是为什么呢?
究其原因就是 a == 300 是判定语句,而 a = 300 叫做赋值语句。前面一句表示判断a的值是否等于300,如果等于,返回1(true),如果不等于则返回0(false)。后面一句表示将300这个赋值给a这个变量,使a的值等于300,然后对a判定,如果a不等于0,着执行 代码块1 ,如果等于0则执行 代码块2
这里,我列出以下常用的判定符
符号 | 意义(什么时候返回真) |
---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
else if()语句
我上面这份代码,看似精妙,但其中却有着一个重要的漏洞,一个很简单的例子就是,当你输入一个大于300的数据时(明显在IOI比赛中不可能实现),但程序依然输出 “还有提升空间” ,这显然是不合理的,虽然你可以在else中再次套入一个 if…else… 语句,但是这样却会显得你的代码十分不简洁
于是乎,else if()语句应运而生,帮助我们解决了这个问题(本质和直接套一模一样)
标准代码样式和图解如下:
if(条件1)
代码块1;
else if(条件2)
代码块2;
else if(条件3)
代码块3;
...
else
代码块...;
具体的判定过程和单个if…else…大同小异,这里就不过多赘述了
switch()语句
除了if引导的分支与判断,C++还提供了switch条件语句
语法规则如下:
switch ( 变量表达式 )
{
case 常量1 :语句;break;
case 常量2 :语句;break;
case 常量3 :语句;break;
...
case 常量n:语句;break;
default :语句;break;
}
但是在使用switch时需要十分注意,必须满足以下规则
只能针对基本数据类型中的整型类型使用switch,这些类型包括int、char、bool等。对于其他类型,则必须使用if语句
switch()的参数类型不能为实型
case标签必须是常量表达式(constantExpression),如42或者 ‘4’、'A’等
case标签必须是惟一性的表达式;也就是说,不允许两个case具有相同的值
当case中没有满足条件的,则执行default语句
除去特殊情况,结尾的break语句时十分必要的
break语句必要的原因
switch就如同它的名字一样,是一个相当于开关的语句,且只可以打开,不可以关闭
而每一个case都是一个独立的开关
当任意一个case开启(即符合条件),那么从当前行开始,代码将严格按照顺序结构来运行,因为其不可关闭的特性,无论后面的case是否满足,switch都将保持开启特性,逐行运行代码,所以,我们在多数情况下,在执行完一个case的内容后,需要及时使用break语句来跳出swtch,以免后续不必要的语句被运行#include<cstdio> using namespace std; int main() { int a; printf("请输入你的IOI分数:"); scanf("%d",&a); switch(a) { case 300 : printf("参见AK大佬\n"); default : printf("还有提升空间\n"); } return 0; }
在上面这个代码中,当我们输入300
可以看到程序输出为参见AK大佬 还有提升空间
显然,第二句是不被我们需要的
那么我们加上break后,再次运行#include<cstdio> using namespace std; int main() { int a; printf("请输入你的IOI分数:"); scanf("%d",&a); switch(a) { case 300 : printf("参见AK大佬\n"); break; default : printf("还有提升空间\n"); break; } return 0; }
当我们再次输入300,可以看到,程序输出了
参见AK大佬
而这才是我们需要的答案
循环
试想一下,我们需要对成千上万,乃至上亿组的数据进行分析,或者对某些数据以同样的规则进行多次变换,我们现有的顺序和分支结构就显得苍白无力了
于是,我们万能的C++为开发者们提供了强大的循环结构,使得我们的一些运算可以被多次反复的执行
在此,我们以一个数学问题引入:求1+2+3+4+…+100的和,由于你不够聪明,所以不知道简便的公式,只能一个一个累加
while()
#include<cstdio>
using namespace std;
int main()
{
int i=1,sum=0;
while(i<=100)
{
sum=sum+i; //或者写为 sum+=i;
i=i+1; //或则写为 i=i+1;
}
printf("%d",sum);
return 0;
}
在上述代码中,核心部分是
while(i<=100)
{
sum=sum+i; //或者写为 sum+=i;
i=i+1; //或则写为 i=i+1;
}
在这里,我们的循环条件为变量i的值小于等于100时,则执行循环内部的语句
当i的值不再满足条件时,跳出循环,执行后续的语句
特别注意
对于循环的判定条件,一定要再能够满足可以在经过运算后能够跳出循环体或者在循环体内能结束程序
否者会出现所谓的死循环,这样的死循环是十分危险的,它会持续的、高强度的占用系统性能
其不但又时会被系统检测到并强制结束,甚至能够导致电脑的崩溃
不怕死的同学可以试试下列代码,并打开任务管理器查看cpu占用#include <cstdio> #include <pthread.h> #define NUM_THREADS 4 using namespace std; void* func(void* args) { while(1); //这里就是死循环代码,可以很方便知道,while(1)是不可能被结束的 return 0; } int main() { pthread_t tids[NUM_THREADS]; for(int i = 0; i < NUM_THREADS; ++i) { int ret = pthread_create(&tids[i], NULL, func, NULL); if (ret != 0) printf("pthread_create error: error_code=%d",ret); } pthread_exit(NULL); return 0; }
在这个代码中,我创建了4个线程,每个线程包含一个死循环
理论上可以使你CPU的4个逻辑处理器达到几乎100%的占用
代码的写法大家可以不用了解,现在我们还没有学习的这么深入
do…while()
还是先来一份代码
#include<cstdio>
using namespace std;
int main()
{
int i=1,sum=0;
do
{
sum+=i;
i++;
}
while(i<=100);
printf("%d",sum);
return 0;
}
do…while() 与 while() 十分相似
但是需要注意的是, while() 是当型循环,而 do…while() 是直到型循环
关于二者的区别,我们可以使用下面这一张图片来说明
可以很方便的看出,在以while为代表的当型循环中,只有满足条件,循环体中的内容才能够被执行;而在直到型循环中,无论是否满足条件,循环体内部的代码都将会被运行至少一次 (图片中直到型循环跳出条件打错了,应该把真假互换才正确)
for( ; ; )
相信for循环一定是我们以后的代码中用到的最多的了,从最简单的数据读入到图论前向星建图,for都拥有其与生俱来的简洁优势
一个标准的for循环如下列代码所展示
for(初始化 ; 判定条件 ; 每次循环结束后运行的指令)
相似的,我们上面的代码也可以以for的方式来实现,代码如下
#include<cstdio>
using namespace std;
int main()
{
int sum=0;
for(int i=1;i<=100;i++)
sum=sum+i;
printf("%d",sum);
return 0;
}
相比while,使用for循环,我们的代码变得更加美观简洁了
为了方便大家理解,我们还是一句一句来分析for的构造和其运行情况
for(语句1;语句2;语句3)
{
循环内部
}
在一个for中,语句1仅会在最开始被执行一次,在这里,你可以对需要的变量定义、赋值乃至其他骚操作
然后,for循环会执行语句2并对其进行判定,如果为真,继续执行,如果为假,跳出循环
接下来,for循环会执行我们在循环内部写入的语句,完成一次循环
当该次循环结束,程序执行语句3,完成后再次执行语句2、循环内部代码。。。如此往复直到再语句2中的条件不再满足或人为跳出循环
蛇皮走位的goto
运用goto语句,也可以实现循环的操作,这里就简单的放上代码就好,不另外做详细解析
#include<cstdio>
using namespace std;
int main()
{
int i=1,sum=0;
l1:
sum=sum+i;
i++;
if(i<=100)
goto l1;
printf("%d",sum);
return 0;
}
这里的实现方式其实和do…while是极其相近的
注意,在编写程序过程中,极其不建议大家使用goto语句,因为其违反了代码的逻辑性,但是有时合理的使用,可以大大提高我们代码的编写难度