一、循环
如果说分支结构是计算机做出判断和选择,在智力上的进化,那循环就是不厌其烦的重复和遍历,在速度上的进化。c语言有三种循环结构。
1.while循环
1.1格式:
++while++ ++(条件){循环体;}++
当条件为真时,执行循环体,执行完一次判断一下,为真,重复执行循环体...直到条件不成立(或遇到break;),跳出循环。
1.2例子:

【如果没有{},循环体就是紧跟着的一句。单语句可省略{}】
本例解答:首先判断 n--是否为真,实际上就是判断n是否为真,判断完后再把n减1。n原为4,非0,条件为真,顺道执行n--,此时n=3,执行循环体。打印--n的值,n原为3,--n,先减1再输出,n=2,即控制台输出2。第一次循环完成。
再次判断n--的值,此时n=2,非0为真,并执行n--,n=1后,再次执行循环。打印输出--n,即0。
每三次判断 n--,n=0非真,结束循环。
while,判断比循环总是多一次。当然程序调试经常出现死循环,Crtl+c中断。
再看一个例子:

2.do while循环
2.1格式:
++do++ ++{循环体;}while(条件);++
不管三七二十一,先执行循环体,再判断条件真假,来决定要不要执行。
2.2例子:

本例:先执行printf("%3d",x-=2); 这句x=x-2=1,结果输出1,3d的3表示位数,忽略。
再判断!(--x)是否为真?x=1,--x为0,非0为真,继续执行循环体printf("%3d",x-=2); 输出-2,x=0-2=-2。
再判断!(--x),x=-2,--x为-3,非-3为假,循环结束。
(判断一个量是否为真时,以0表示假,非0表示真,正数、负数都表示真,!2、!(-3)等都表示假,只有!0表示真。)
例子:

编程思路:
总体来说,是个数列求和,需要用到循环语句。如果求到第n项,则循环n次;本题不知道循环次数,要求通过精度来确定循环次数,即第n项小于0.00001时停止循环,可用do{...}while(an>0.00001)循环。
奇数项与偶数项符号不同,可用if语句进行分支判断;也可在前一次循环基础上取负。
参考代码:
float x,a=1.0,n=1.0,s=1.0;
printf("请输入一个大于1的数:");
scanf("%f",&x);
do{
a=-a/x;n=-n/x;
a=a+n;
s=s+a;
}while(fabs(a)>0.00001);//大于精度执行
printf("S=%lf\n",s);
说明:对数列类型的循环,需要搞清楚第n项是在前一项的基础上,是怎么变来的?变化的差异是不是相同的?
本例:后一项的分母,总是在前一项分母的基础上除以x;分子总是在前一项的分子的基础上加1;符号总是在前一项的基础上取负。
所以,a=-a/x;表示分母在前一项的基础上除以x,并且符号在前一项基础上取负;而n=-n/x;表示分子加1的,n开始为1,分母同步变化的部分。
a=a+n;相当于数列的通项式an。
s=s+a;相当于数列求和Sn。
fabs(a)>0.00001,大于精度就执行,如果第n项小于这个数了,就停止。实际执行上,第n项已经执行了,超过了0.00001这个精度。++do while循环是先执行再判断。++
如果第n项不能小于这个数,即不包含0.00001,意思是超过精度就丢弃,那循环就要用到++while(){}语句,先判断再执行。++实际上第n项未执行。
这就是为什么有while与do while的区别。
3.for循环
3.1格式:
++for++ ++(初始值;条件;增量){循环体;}++
**初始值:**通常用i=1或i=0,这个i要先声明:int i;如果初始值在条件范围内,执行循环体。
**条件:**初始值i的变化范围,比如i<20。
**增量:**在执行循环体一次后,计算增量,通常用i++, i自动增1。并再次判断i,如果在范围内继续:执行循环体->增量->判断,直到超出范围退出循环。
典型结构如下:
int i;
for(i=0;i<20;i++){...}
for后面的小括号中要用两个分号";",
3.2 例子:
依次输出1-9一行,数字之间用空格。

i在依次变化过程中,每个节点只与i有关,好比排队,一人一号。
再比如:依次输出1-9一列。

每次输出加个"\n"回车,好比电梯,一层一停。
3.3循环+判断
题目:所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个"水仙花数",因为153=1的三次方+5的三次方+3的三次方。编程输出所有水仙花数。
编程思路:这个题目,显然是把所有的三位数(100-999)依次拿来计算验证,看是不是符合水仙花数的规律,是则输出。所有三位数依次进行验证,涉及到循环。循环有三种:++while(.){.}++ 、++do{.}whie(.)++ 、++for(.){.}++三种,用哪种好呢?如果次数是确定的,用for;如果满足条件才做,用while;如果先做再判断条件,用do while。显然本例用for更好。
本例,在每次循环中,都要计算一次,看结果符合不,这涉及到判断,用到if语句。编程如下:

if作判断时,先要把百位、十位、个位上3个数字取出来,再分别三次方,然后求和,最后比较。
【 "循环+判断"是电脑程序最美丽的存在,**++循环里面套分支++**是最常用的手段。循环体现"快",比如图书馆有三百万册书,把含有"生命"的书名找出来。如果我们用300个人来查找,每人查找10000册,可想有多慢。如果是电脑,把300万条书名循环一遍,只要一瞬间。判断体现为"智",通过计算、比对,筛选出有用的部分。】
再比如输出10以内的奇数:

再比如矩阵输出1-100:

3.4双层for结构的矩阵输出
如果说单层for是线性结构,那双层for就是平面结构,可以很轻松输出一些矩阵花样。
双层for结构。如下:
int i,j;//双层for ,声明两个变量
for(i=1;i<10;i++){
for(j=1;j<10;j++){
...}..}
双层for结构,用i控制行,用j控制列,对于在屏幕上输出各种花式图案,是个不错的选择。双层for是一个二维结构,i好比第几层楼,j表示一层楼里的第几个房间;在排队中,i代表第几个队,j表示一个队里的第几号。简单理解:i是主干号,j是主干里的分支号。如果需要三层、四层,就再引入k,m等,用k表示第三层编号,m第四层编号。
比如输出一个5*5矩阵:

i=1时,先运行j这个单层,分别是j=1...j=5,完了,加个回车,再上二楼i=2,再次运行j这个单层,又是j=1...j=5,...直到i=5运行完毕。
有时,++充分利用i与j之间的关系++,挖掘i与j变化相关性,可以输出一些特殊变形。

如果要求输出这样一个形状,怎么做?
观察发现:双层循环,当i=j时,输出"*",其他输出空格,代码如下:

如果是下面这种情况呢?

什么规律呢?i+j=常数,每个*到上边、左边的距离和为定值。代码如下:

如果是下面的图案呢:

显然,紧跟上例,改条件为:j+i>=10。还有

如果是下面的图案呢,又是什么规律?

找规律:从矩阵(我们也可以叫方阵)的第一个位子开始,打一个,空一个,从左到右,再从上到下。这正好是所说"奇偶性",把i+j的和当作一个数,如果这个数是偶数,就打"*",否则打空格。条件语句可以这样写:if((i+j)%2==0)printf("* ");完整代码如下:

以上只是几种最基本的造型,对于更复杂的图案,需要分别计算空格和符号"*"变化规律与i、j之间的联系。比如:



紧紧抓住横坐标j、纵坐标i之间的关系。
如果是平面几何的图形,比如输出一个圆,怎么弄呢?
我们首先要模拟一个坐标系,大致确定坐标原点o的位置,比如:x的范围-10~10,y的范围-10~10;然后再根据图形的边界调整这个范围。比如单位圆的边界为1,那x、y就调整到-1.5~1.5,反正大于1,也不能太大,不然不成比例,细节丢失。接下来,设置一个变化量△d,去分切这个范围。分切把握一个原则,分切的数量小于80,因为C语言输出屏宽默认为80。另外要注意,x水平字符间距小,y垂直字符间距大,变化量△d要作调整(通常2倍)。代码如下:

再来两个例子:


题目:输出9*9口诀(乘法表)。

这是一个典型的双层for循环,并且输出呢,只输出整个矩阵的一半,即j(列)<=i(行)的情况。第一层循环:行数9行,设置条件:for (i=1;i<10;i++),第二层循环:列数是变化的,依次从1到9,设置条件:for(j=1;j<i+1;j++),注意条件"j<i+1",与"j<=i"等价,即包含j=i的情况。
完整代码如下:

"%-3d"表示整数输出,格式三位宽度,并且靠左对齐。其实"两位+空格"也可。
二、自定义函数
C语言就是一个个函数,从main()开始,你调用我,我调用你。系统默认的(比如:printf()、scanf()、if()、for()、while()...等),括号即函数,我们拿来用就行。
如果我们有一个功能块(一段程序,完成某个功能),要在不同地方多次调用,你不能总是复制粘贴到这些地方,那样会造成程序臃肿,参数过多,难以阅读。
正确的做法是:把这个功能块定义为一个函数,取个名字,调用时喊名字就行。这样主次分明,结构清晰,易读。同时呢,这种函数结构使C变的很容易扩展。比如一个数开平方,很多人各写一段代码来完成,通过专家团评审,其中一种最好。我们就把这个最好的代码段定义为函数sqrt()。同样的方法,对数函数log()、指数函数exp()...最后将这些函数打包,在程序开始用#include<math.h>声明。当然函数扩展可以包含很多方面:图像处理包、语音处理包、人脸识别包、网页处理包、AI包...等等。很多东西不需要你具体去写,你拿来用就行,减少了程序开发时间,提高了开发效率。
1.C语言++自定义函数++语法结构。
++类型 函数名(参数){代码段;}++ 比如:

void(空的) 表示类型为无类型,即不需要返回任何类型的值。此类型函数直接出结果。
int、double、char表示返回值的类型。在{}中的代码中要加入return 语句,表示返回函数中的哪个值。为什么要有返回值呢?因为返回的值只是个中间值,后面还要参与其他操作运算,是可以给它安个变量名的。
a()即为自定义函数名,跟定义变量名一样,任意取,但不能与C本有的函数重名。为便于记忆,通常用英文单词或缩写,比如求和sum()。
(参数)小括号中的参数可有可无,根据需要而设。设定时同样要声明类型如(int a)(double x)(int a[])等。这种参数只对该函数有效,在内部使用,在其他地方(主函数、其它函数)无效,又叫形式参数(形参)。
下面举例说明:

void类型不用返回,直接出结果。当然换成int,不加return也是可以的。
函数调用,输名字即可,有参数带参数,无参数带括号。
有返回值的函数,相当于一个变量名,可以传给其他变量,参与计算,也可出现在其他语句之中。


2.全局变量的顺序。
如果在前面在函数外已声明的变量,在函数内使用即可,如下:

如果在自定义函数++后面++声明变量,通不过,如下:

怎么办?这时需要用到++extern(外来的)++作声明,同时这个int ss=2;放在main()里也是不行的:

要放到main()主函数的外面:

自定义函数sum()也可跟main()平级,不需要被包含在main()之中。
这个"int ss=2;"中ss不属于哪一个函数,属于文件,对全局有效,又称全局变量。上例中"int s;"中s只属于sum()函数,叫局部变量或动态变量。
3.形参和实参。

上例中,自定义函数sum()中的a、b叫形参,函数内部使用,离开函数不能使用;c、d叫实参,出现在主调函数中,把数字传递给形参,在函数内不能使用。
4.静态变量。
函数中的局部变量,动态分配 存储空间,函数调用结束时,会释放这些存储空间,变量的生存周期结束,变量值失效。如果想保留某个关键变量的值,可以用static(静止的)进行声明。

可以看出,这个s产生了记忆,每调用一次函数,它的值不会消失。至于逆序输入,应该是先全部计算完,压入堆栈,最后一次性输出。
5.函数的递归调用。
在定义函数时,在语句中加入一句,调用自己,就是递归。咋一看,不是产生了死循环吗?是的,加个退出条件即可。递归在某些特定的场合很有用处。

三、二级菜单
菜单的设计,是方便与计算机交流的主要方式,包括登录、主菜单、二级菜单等。程序设计会用到循环、分支、函数等。举例如下:
//二级菜单设计
int login(){ //登录函数
printf(" 月球资源开发有限公司管理系统 \n");
printf("============================================\n\n");
printf(" 请输入管理员账号:");
char id[20],pw[20],id1[]="huang",pw1[]="123456";
gets(id);
int i=0;
printf(" 请输入管理员密码:");
while ((pw[i]=getch())!='\r') {// 当输入回车时停止读取密码
printf("*");// getch()输入不回显,\r结束,也有_getch()
i++; // getchar()输入回显,\n结束
}
pw[i] = '\0'; // 结束字符串
if( strcmp(id,id1)!=0||strcmp(pw,pw1)!=0){
printf("账号或密码错误!\n");
return 0;
}
}
int menu(){ //主菜单函数
system("cls");
printf("月球资源开发有限公司管理系统 \n");
printf("============================================\n\n");
printf(" 主菜单\n\n");
printf(" 1.功能A模块 3.功能C模块\n");
printf(" 2.功能B模块 4.功能D模块\n");
printf(" 0.退出\n");
printf("请选择:");
}
int submenu1(){ //子菜单1函数
system("cls");
printf("\n月球资源开发有限公司管理系统 \n");
printf("============================================\n\n");
printf(" 功能A子菜单\n\n");
printf(" 1.A1模块 3.A3模块\n");
printf(" 2.A2模块 4.A4模块\n");
printf(" 0.返回\n");
printf("请选择:");
}
int submenu2(){ //子菜单2函数
system("cls");
printf("\n月球资源开发有限公司管理系统 \n");
printf("============================================\n\n");
printf(" 功能B子菜单\n\n");
printf(" 1.B1模块 3.B3模块\n");
printf(" 2.B2模块 4.B4模块\n");
printf(" 0.返回\n");
printf("请选择:");
}
login();
int choice,subchoice;
while(1){
menu();
scanf("%d",&choice);
switch(choice){
case 1:while(1){
submenu1();
scanf("%d",&subchoice);
switch(subchoice){
case 1:printf("这是A1功能。");system("pause");break;
case 2:printf("这是A2功能。");system("pause");break;
case 3:printf("这是A3功能。");system("pause");break;
case 4:printf("这是A4功能。");system("pause");break;
case 0:break;
default:printf("无效选择。");
}
if(subchoice==0)break;
}break;
case 2:while(1){
submenu2();
scanf("%d",&subchoice);
switch(subchoice){
case 1:printf("这是B1功能。");system("pause");break;
case 2:printf("这是B2功能。");system("pause");break;
case 3:printf("这是B3功能。");system("pause");break;
case 4:printf("这是B4功能。");system("pause");break;
case 0:break;//退出switch
default:printf("无效选择。");
}
if(subchoice==0)break;//退出while
}break;
case 3:break;//略
case 4:break;//略
case 0:return 0;
default:printf("无效选择。");
}
}