前言
之前学过C,但是太久没用了,忘得差不多了,现在要学C++,顺带温习一下C,偶然看到这本书。虽然我学过C,算是已经入过门,但是觉得不妨重新以一种新的、十分具象化的视角去看待一下,因为之前我学过,那是我浅显的理解,现在和入门书浅显的语言比较看看,或许思路上会得到一些拓展,不再局限于之前的课本上学到的那种表达和说法。
《啊哈C语言》是第一版,《啊哈C语言!逻辑的挑战(修订版)》是第二版,章节上主要是第二版的第一章改成引导章唤起学习编程的兴趣和介绍受众。之前主要是照着第一版写的,但是写到for章节那里后,找不到第一版的后续章节了,就重新按第二版的章节号排版。
用的软件是codeblocks
第二章
第一节~第六节
多少个输入,多少个输出,存储什么,计算什么。(这个分析思路虽简单却很有效)。
那个将变量,看作是小房子的,很具象化,虽然不是很准确。
=
是赋值运算符,而不是等于的意义,看例子:
int a=7;//现在a这个小房子(变量)的值是7 a=a+1;//*号的运算级别优先于赋值号“=”,所以计算机先执行a+1,此时a的值仍是7,所以a+1的值是8;紧接着计算机执行赋值语句,把计算出来的值8再赋值给a,此时a的值就更新为8了。
printf语句只会输出双引号里面的内容,双引号后面的内容知识对双引号内的部分起到补充说明的作用。(理解了这一点就不会那么容易忘记printf的语法格式了;同时也理解scanf("%d %d",&a,&b);
这个输入a,b时是用空格分开了)
- 代码方面,学到了
system("coler xx");
这个,xx是两个十六进制数。第一个代表队是底色,第二个代表字体颜色,也可以只有一个十六进制数,那它是代表的字体颜色。
第七节
scanf的使用,生动形象说清楚了为什么scanf要用上取地址符&,而printf不需要。
第八节
float浮点型,double存放极大和极小的浮点数。输出上,float数据类型用的是%f,而double用的是%lf。
char字符型,265种字符。
第十节
交换两个数的值。除了借助第三个临时变量,还可以将a变量换成用原来的a,b变量来表示a=b-a
,然后b=b-a
这就让b获得了a的值;最后a=b+a
,就是(b-a)+(a)。
第十一节 让代码看起来更美
- 合理使用Tab键
- 注释作用:说明或“临时性删除”
- 定义变量并赋初值,可以直接写到一起。
int a;a=1;
可以简写成int a=1;
;多个变量同理int a=1;b=2;c=3;
;浮点和字符型也类似,float a=1.1;char c='x';
第三章
第二节
算法,其实就是解决问题的方法(千万不要被这专业名词给吓住了)。
每当我们遇到一个问题的时候,我们首先需要思考的是:解决这个问题的方法和步骤(算法)。这里只是将方法的每一步用C语言描述实现。【无论是人类的自然语言还是书写的语言、计算机语言,语言,就是某一/各种思维的实现、具象化】
第三节~第五节
判断一些奇偶数、尾数,可用%
(mod,也叫取模)求余运算。如判断一个数末尾是否为7【即这个数的个位为7,即被10除后余数剩7】:if (a%10==7) printf("yes"); else printf("no");
在c语言中,当除号/
左右两边都是整数的情况下,商也只有整数部分。如5/3=1......2,商是1,余数是2。2/3的商是0。
求商和求余
/ 和 % 的用法。
注意 / 中,两个%d的值为整数,即取整。有%f类的,就是商了,带小数的。
判断键盘输入正整数是否是个位数;判断约数
题目:从键盘输入一个正整数,让计算机判断这个数是否为一个“一位数”(1~9之间),如果是则输出yes,否则输出no。 求商和求余 / 和 % 的用法。 在c语言中,取模运算符`%`参与运算的量要求均为整型。并且参与运算的量可以为负数。取模运算的结果等于两个数相除后的余数。 对除号`/`左右两边都是整数的情况下,商也只有整数部分(商取整)。如5/3=1。而有%f类的,就是商了,带小数的。*即参与除法运算的量均为整型时,结果为整型,舍去小数。如果运算量中有一个为实型,结果为双精度实型。* 由此,我们想到,1/10=0,2/10=0,...9/10=0,10/10=1。那么一个整数除以10的商不是0,就说明它有十位,它对10取商就不会是0。 上面的思路用c语言具象化描述出来: int main() { int a; scanf("%d",&a); if (a/10==0) printf("yes\n"); else printf("no\n"); return 0; } 在解决问题的过程中熟悉语言语法,不然之前一直不太懂求商。 /******************************************************/ 题目:从键盘输入两个正整数,让计算机判断第2个数是不是第1个数的约数。如果是 则输出yes,不是则输出no。 知识储备:如果x除以y的商是整数(x能被y整除),那么y是x的约数。 即x%y==0,那么y就是x的约数。 思维的具象化: main() { int x,y; printf("输入两个正整数:"); scanf("%d %d",&x,&y); if (x%y==0) printf("第二个数是第一个数的约数\n"); else printf("no\n"); }
第六节
判断闰年
某一年是闰年的条件:能被4整除,但不能被100整除,或能被400整除
这篇文章讲得很详细:判断闰年的3种方法(判断+范围输出)
我是这么写的:
scanf("%d",&x); if ((x%4==0)&&(x%100!=0)||(x%400==0)) printf("这年是闰年\n"); else printf("no\n");
第七节
三个数大小比较
- 使用方法①时发现输入1 3 2时结果是2 3 1,调试时发现跳过了
(ma>c>mi)
这一条件?奥,是我写的语法不对,故跳到else
语句里了。
点击查看代码
int a,b,c,ma,m,mi; scanf("%d %d %d",&a,&b,&c); //a=1;b=3;c=2; if (a>b) { ma=a;mi=b; } else { ma=b;mi=a; } if (ma>c&&c>mi) { m=c;//printf("排列1:%d %d %d",ma,m,mi); } else if (c<mi) { m=mi;mi=c; } else { m=ma;ma=c;//printf("排列2:%d %d %d",ma,m,mi); } printf("三个数降序排列:%d %d %d",ma,m,mi);
if (a>b) { a=a;b=b; } else { t=a;a=b;b=t; } if (a<c) { t=a;a=c;c=t; } if(b<c) { t=b;b=c;c=t; } printf("三个数降序排列:%d %d %d",a,b,c);
其实上面a>b
的条件可以不用写,因为这时a,b就是降序排列了。更好看点:
//思路也更清晰:先是a,b,a是较大的,然后a,c,也把较大的放到a,那么这个时候经过这两轮,a一定是最大的数,随后再比较b与c谁大。 if (a<b) {t=a;a=b;b=t;} if (a<c) {t=a;a=c;c=t;} if (b<c) {t=b;b=c;c=t;}
题目:从键盘读入一个整数,若是奇数就输出这个数的后三个数;若是偶数就输出这个数的前三个数。
知识储备:判断奇偶,看能否被2整除。如0,2,4是偶数,1,3,5是奇数。
C语言的(逻辑)与或非:&&,||,!
。按位与、或、异或:&,|,^
。
- 传统的利用取余进行判断
scanf("%d",&a);//判断a的奇偶性 if(a%2==1) printf("%d %d %d",a+1,a+2,a+3); else printf("%d %d %d",a-1,a-2,a-3); return 0; 或者: if (a % 2 == 1) { //n为奇数} if (a % 2 == 0) { //n为奇数}
- 按位与。0&0=0; 0&1=0; 1&0=0; 1&1=1。因为由1去按位与,奇数最后肯定是1;偶数肯定是0,所以可以这么操作。
if((n & 1) == 1){ // n 是个奇数。 } if((n & 1) == 0){ // n 是个偶数。 }
- 按位异或。因为00=0;01=1;10=1;11=0。由1去按位异或,奇数最后肯定0;偶数肯定是1,所以可以这么操作。
if ((1^n) == n - 1){ //n为奇数 } if ((1^n) == n + 1) { //n为偶数 }
题目:从键盘任意读入四个整数,将其从小到大输出。
类比三个数的排序,先确保最大的,再确保次大的,慢慢,最小的会自动排出来
if (a<b) {t=a;a=b;b=t;} if (a<c) {t=a;a=c;c=t;} if (a<d) {t=a;a=d;b=t;} if (b<c) {t=b;b=c;c=t;} if (b<d) {t=b;b=d;d=t;} if (d<e) {t=d;d=e;e=t;}
六轮比较
第九节
在C语言中,当对某一个数进行讨论真假的时候,只有0是假的,其余都认为是真的(非空则为真)。如1,-12,是真的。
第十节
在if-else语句,else的匹配采用就近原则。且if部分和else部分可以含有自己的子语句,其实本质上就是一条语句——“if-else复合语句”,if-else语句并不知道if条件为真时里面的语句是什么样。这时,大括号是可以省略的,那样代码就会没那么臃肿。另,如果只有一条语句,也可考虑不用大括号,不过要是考虑对称,那要也是有道理的。
第四章
第二节
可见,有限次数的循环(while,for),需要借助变量,让变量从a变到b,所以此变量需要赋初值,同时变量在执行循环体内的操作后要有变化,否则会死在循环体里。
题目1:打印1~100。
int a; a=1; while (a<=100) { printf("%d\n",a);// a+=1; }
题目2:打印1~100内的偶数
- 我想到了两个循环,因为从1~100,第一判断范围,第二判断偶数,是两个判断条件:
int a; a=100; while (a>=1) { while(a%2==0) printf("%d\n",a); a--; }
但是我发现结果是一直在打印100,奥,看来是在while子循环里,跳不出来,我应该在子循环加上花括号,包含a--;
结果发现只打印了一个100,也没有“按任意键退出”的句子。
再仔细思索,a是100,进入子循环,能整除,打印后,a变成99,不满足子循环条件,会跳出,到达外循环,外循环却没有任何操作,那就是死在了外循环。那么,也就是外循环也需要a的值变化,重新写成这样,就可以了:
int a; a=100; while (a>=1) { while(a%2==0) { printf("%d\n",a); a--; } a--; }
- 而书上的思路是,之前a是从1~100,现在要输出偶数,那a从2开始,且每次变化2,不就能完成输出了吗。
int a; a=2;//改变a的初值 while (a<=100) { printf("%d\n",a); a+=2;//改变a的变化值 }
题目1很简单,但是变化到题目2的时候,我的思路却没跟上,不能很好利用第一个题目的思路。
题目3:打印1~100以内3的倍数。
思路(方法步骤/算法):a从3开始,每次加3。或者:两层循环,遍历1~100,第二层判断条件是a%3==0
。
题目4:让计算机从1打印到100,再打印回1。如1,2,...100,99,...3,2,1
(思路)算法:两个循环并列。先让a从1加到100,然后并列的第二个while循环让a从100减到1。
int a; a=1; while (a<=100) { printf("%d\n",a); a++; } a=a-2;//a-=2 while(a>=1) { printf("%d\n",a); a--; } /**********或者两个while这样写:**********/ while(a<100){ printf("%d\n",a); a++; } while(a>=1){ printf("%d\n",a); a--; } /**********或者只用一个while,借助if实现*********/ /*程序思路:将i初始化为1,将a递增并打印出到100。在a=100时将i的值改为-1,打印回0便停止。*/ int main(void) { int a = 1; int i = 1; printf("Press Enter to continue.\n"); int b=getchar(); while (a <= 100) { printf("%d ", a); a = a + i; if (a == 100) { i -= 2;//将i的值转化为-1,每次减1 } else if (a == 0) { break; } } return 0; }
第三节 if对while说我对你很重要
在打印之前,对变量a的值进行判断即可。具体来说:当变量a的值是3的倍数时就不输出,否则就输出。而判断可以用if语句。
题目1:计算机输出1~100内除去3的倍数的数。
算法1:两层循环,两个条件。外循环让a从1遍历到100,内循环则判断不是3的倍数就不打印。
int a; a=1; while (a<=100) { while(a%3!=0)//需要改成 while(a%3!=0&&a<101),否则会从1打印到101 { printf("%d ",a); a++; } a++; }
发现这个运行结果会打印到101,因为a是100时,打印后内循环加到101,跳出内循环,到外循环再+1,然后判断a大于100了,循环结束。外循环改成(a<=99)
,那就打印不出100,那得在内循环限定(a%3!=0&&a<101)
,再运行,通过。
突然感觉所谓的bug,就是我们分析算法的时候,对那种语句的细节的把控没想到。这个双层while循环其实每次从内层循环跳出到外循环,a的值都会变化2。
算法2:第二个判断是不是3的倍数不用while循环,而用if。好处是,少了一层循环,运算速度会更快。
int a; a=1; while (a<=100) { if(a%3!=0) printf("%d ",a); a++; }
题目2:输出1~100之间能被3整除但不能被5整除的所有数。
算法:在题目1的基础上,条件改为“能被3整除并且不能被5整除”这一条件即可。
- 使用双层循环的内循环条件:
while(a%3==0 && a<101 && a%5!=0)
- 使用if作为判断条件的:
if(a%3==0 && a%5!=0)
运行结果:3 6 9 12 18 21 24 27 33 36 39 42 48 51 54 57 63 66 69 72 78 81 84 87 93 96 99
题目3:游戏:从1开始报数,每遇到7的倍数或者末尾含7的数,就不能报出。如7,14,21,27,28等。现在要知道1~100内有多少个这样的数。
算法:如果a是7的倍数或者末尾含7,可以这样表达:a能被7整除或者a对10取模等于0。
那么C语言中把if的判断条件写成if (a%7==0 || a%10==7)
即可。
第四节 求和
思路的变化:
从a=1+2+3;printf("%d",a);
到a=0;a=a+1;a=a+2;a=a+3;
再到a=0;a=a+i;
,i从1循化加到3。代码:
int a,i; a=0; i=1; while(i<=100) { a=a+i;//***执行前可加入if判断 i++; } printf("%d",a);
1. 题目1:求1~100内所有7的倍数或者末尾含7的数的总和。
这个时候,i不需要每次都把搬的苹果放到篮子a里。只需要把符合条件的放到篮子a里即可。即在执行a=a+i;
前,先判断要不要这个i,if (i%7==0 || i%10==7)
求1*2*3*4...*10的乘积。(结果是3628800)
int a,i; a=1; i=1; while(i<=10) { a=a*i;//这里用a=a*(i+1)也可以,但是判断条件要变成(i<=9),不然就是11的阶乘了。 i=i+1; }
2. 题目2:求1~100之间所有偶数的和。(结果是2550)
方法1:i只把偶数放到篮子a里面。即if条件句来判断(i%2==0)。方法2:直接从2加起,后面i依次+2。
点击查看代码
/***方法1***/ int a,i; a=0; i=1; while(i<=100) { if (i%2==0) { a=a+i; } i=i+1; } printf("%d",a); return 0; /***方法2***/ int a,i; a=0; i=2; while(i<=100) { a=a+i; i=i+2; } printf("%d",a); return 0;
3. 题目3:输入一个整数n(1<=n<=9),求n的阶乘。
点击展开代码
``` int a,i,n; a=1; i=1; scanf("%d",&n); while(i<=n)//这个代码其实可以求任意n的阶乘,要是非要限定的话,那么条件改为i<=9 { a=a*i; i++; } printf("%d!is%d",n,a); return 0; ```第五节 60秒倒计时开始
“暂停”功能,Sleep
语句;“清屏”功能,system("cls")
语句。
#include <windows.h>
windows环境下增加这个头文件,并且Sleep函数的S是大写的。
3s倒计时
#include <windows.h>//windows环境下增加这个头文件,并且Sleep函数的S是大写的。 printf("3"); Sleep(1000);//一定是大写,单位是毫秒。 system("cls"); printf("2"); Sleep(1000); system("cls"); printf("1"); Sleep(1000); system("cls"); printf("0");
60s,比较久,可以用之前的从100打印1的循环思想:
int i; i=60; while(i>=1) { printf("%d",i); Sleep(1000); system("cls"); i--; } printf("%d",i);
这里如果把清屏语句放到循环的最前面,打印留在最后,就不用循环体外另外加上0的显示了:
//system("color 0a");//屏幕变成黑底绿字,更酷了。 int i; i=60; while(i>=0) { system("cls"); printf("%d",i); Sleep(1000); i--; }
由此可见,顺序(执行的步骤)是很重要的,轻则影响程序简洁性与速度,重则程序错误。
题目:尝试编写一个两分钟的倒计时。形如:2:00 1:59 1:58 .......0:01 0:00
算法:两个变量,一个是从2到0,只循环3次(a>=0并且b为00,这个b定义为char型。之前有a-1可以输出前一个字符的例子。);一个从59到00,每一次外循环有效时都循环。即使用循环嵌套。(从59到00再到59,可以用两个并列循环,)
字符型?有点行不通,我觉得难点在于“00”的显示,计算机会显示成“0”,那么,就需要3个变量(后面看了别人的代码,发现完全可以指定%.2d这样去显示00出来,以前只见过%4.2f浮点型这样用【参考1】;或者当小于10的时候,打印用0%d
这样在打印格式里预先加个0【参考2】),a:bc这样对应2:00。那么a从2到0,b是5-0,c是0-9的循环。如果a>=0并且b==0,c==0
,那么a-1,进入while里b、c的两层循环。(b==0&&c==0
)b、c分别从5、9开始循环。
while(a>0) { //Sleep(59000); system(cls); printf("%d:%d%d",a,b,c); a--; while(b>=0) { if(c>=0) {c--;} else if(c==-1) {b--;c=9;} else if(b==-1) {b=5;break} Sleep(1000);system(cls);printf("%d:%d%d",a,b,c); } }
先打印出2:00,一秒后,a-1,b为5,c为9,打印;接下来c-1,一秒后,打印。
然后c--,一秒后打印;
c--,一秒后打印;
......
c--到0,b--同时c变成9,打印。
c减到8,一秒后打印;
这时两者都为0的时候,a再减1;下一秒,b,c各自赋值。
debug:啊,我对if-else分支逻辑错误了,他们就是三条并列的岔路口,走了一条就不能再走另一条了。
if(c>=0) {c--;} else if(c==-1) {b--;c=9;}//当c==0是,走完上面的if,不会再走下面的else if了,c=-1,但是b--;c=9;这两个语句不会执行到。
- 经过3、4次调试,我终于做出来了!总结整理一下就是,a,b>=0时,如果c>0,那就c--;如果
c==b==0
,那b,c重新赋值而a--;如果c==0
而b!=0
,那就c重新赋值和b--
。然后发现最后停止的是-1:59,那那就在a==b==c==0
经历a--后a=-1,这个时候就跳出while循环即可(我用了break语句跳出)。C语言中文网的break与continue的语法 - 这里感觉还是画画流程图更快、更清晰。
- 哈,结合之前从1打印到100再打印回1那个题,发现两个数的循环只需要if-else语句配合上变量的变化(当然if-else语句要在循环里才有效),就不用多一个循环了。
点击查看代码
int a = 2,b = 0,c = 0; system("color 0a"); system("cls"); printf("%d:%d%d",a,b,c); while(a>=0 && b>=0) { if(c>0) {c--;} else if(c==0 && b!=0) {b--;c=9;} else if(b==0 && c==0) {b=5;c=9;a--;if(a==-1)break;} Sleep(1000);//光看效果,可以调成50。 system("cls"); printf("%d:%d%d",a,b,c); }
从网上参考到其他人的:
- 【参考1】思路是2分钟倒计时,就是120s的倒计时,根据之前60s的倒计时改下变量即可。不同之处在于显示,这里是“分钟:秒”的显示,因为秒(s)和分钟(min)是60进制的关系,那秒除以60即可得到分钟。
/*来源:https://zhidao.baidu.com/question/541157422.html*/ #include <stdio.h> #include <stdlib.h> #include <windows.h> int main() { int a,b,c; a=120; system("color 0a"); while(a>=0) { system("cls"); b=a/60; c=a%60; printf("%d:%.2d",b,c); a=a-1; Sleep(1000); } return 0; }
- 【参考2】他是用的两层for循环,打印输出小于10s时巧妙地用0%d这样的格式。
/*来源:https://blog.csdn.net/m0_52094853/article/details/109681925 作者:_扬帆起航 时间:2020-11-13*/ #include<stdio.h> #include<Windows.h> int main() { int i,j; system("color ea"); for(i=1;i>=0;i--) //i表示分,j表示秒 { if(i>=1) { printf("2:00"); Sleep(1000); } for(j=59;j>=0;j--) //在这个for循环里秒从59到00变化,变化完后,回到外循环,继续i-1实现分钟减一 { system("cls"); if(j<=9) { printf("%d:0%d",i,j);//巧妙的打印格式 Sleep(1000); continue; } printf("%d:%d",i,j); Sleep(1000); } } return 0; }
- 【参考3】这个结合了上两个参考各自的优点———60进制和巧妙的输出,变量就很少。
/*———————————————— 版权声明:本文为CSDN博主「sez020730」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/sez020730/article/details/125354080*/ int main() { int a; a=120; while(a>=0) { system("cls"); if (a%60<10) printf("%d:0%d",a/60,a%60);//这是针对秒数小于10时的打印格式。 else printf("%d:%d",a/60,a%60); a=a-1; Sleep(1000); } return 0; }
第六节 循环嵌套来了
1. 尝试打印图形
。
简单的如手动三行printf;循环,不知道为什么,一想到循环我就想到了两个循环(这个在我之前看到过程中可以体现),一个负责行的重复打印,另一个负责列的重复。其实前面也说了if-else
搭配循环会很好用,有时反而你的思路不会那么乱。
点击查看书上代码参考
/*思路:一共需要输出15个星号,所以只需要循环15次,但是每行只需要5个星号,即每打印5个星后就要换一行,可以通过if语句来控制换行。*/ a=1; while(a<=15) { if(a%5==0) printf("\n"); printf("*"); a++; }
int i=5,j=3; while(j>0)//j的循环控制换行 { while(i>0)//i的循环控制每行打印5个 { printf("*"); i--; } if(j==1)//加这个if条件是不想最后有一行换行 { j--; continue; } printf("\n"); j--; i=5; }
while(j>0) { while(i>0)//每行打印5个 { printf("*"); i--; } j--; i=5;/*因为每次都要重新让i=5,所以这句也可以放到while(i>0)语句的前面,不用int i=5;这样去赋初值*/ if(j!=0)//加这个if条件是不想最后有一行换行 printf("\n"); }
2. 题目变换:打印如图
思路:首先可以看到行数是5行,则控制行数的循环是5次。内循环的*号数,发现和第几行相关(即和外循环的变量相关),逐行递增。
int i,j=1;//列与行 while(j<=5)//5行 { i=1; while(i<=j)//每行打印j个 { printf("*"); i++; } if(j!=5)//if条件控制最后第五行不换行 printf("\n"); j++; }
3. 题目:用while循环按如下形式打印图形
1
2 3
4 5 6
7 8 9 10
11 12 13 14 15
思路:总共15个数,在第j行输出j个数后换行。
int i,j=1;//列与行 int a=1;//要打印的是一个数,i每次循环会置1。 while(j<=5)//5行 { i=1; while(i<=j)//每行打印j个 { printf("%d ",a); a++; i++; } if(j!=5)//if条件控制最后第五行不换行 printf("\n"); j++; }
如果我不是被一步步引导,我要如何快速想出来呢?
若是重复打印,且每一行相同的列数,那么while里用if判断一下换行即可;
若是重复打印,每一行逐行递增列数,那么需要用到2层循环,外循环控制换行,内循环控制打印的列数;
所谓打印显示,可以是*、行数、自增的整数,就把它们看作是一个个图形即可。
第七节 奔跑的字母
清屏,Sleep函数,可以实现一定时间间隔的字母闪现,但是如何控制它出现在第一行第二列、第一行第三列?想到了重复打印空格。
思路:第一秒打印H,清屏;第二秒打印一个空格,再打印H,清屏;第三秒打印2个空格,再打印H。可见,空格数目x逐秒增加——>用while循环控制打印空格的数目。打印H前先通过循环控制打印好空格。
int j=1;//第几列 int x=1; int time=1000; while(x<10) { while(x<j)//此循环控制打印空格的数目 {printf(" ");x++;} printf("H"); Sleep(time); system("cls"); j++; }
debug发现,第2秒的时候,x=2,j=3,那么内循环判断时只一次过后,x=3=j了,也就是只打印了一个空格,解决方法就是每次内循环判断前的x的值要是1,这样才可逐秒增加。然而又发现问题:不给x赋初值,x得到随机值,这样while循环可能直接就进不去。
再次修改:
#include<stdio.h> #include <stdlib.h> #include <windows.h> int main() { int j=1;//第几列 int x=1;//空格数目x int time=1000; while(x<10) { x=1; while(x<j)//此循环控制打印空格的数目 { printf(" ");x++; } printf("H"); Sleep(time); system("cls"); j++; } return 0; }
这样“H”就走了9步。
第八节 究竟循环了多少次
这个就先看外循环,再看内循环。注意循环会有初值,每次执行完后判断条件里的变量一定会改变,否则跳不出循环。进入条件和退出条件都要有。
/*下面的代码,初看我以为会打印10!*10次那么多,后面运行,发现是1+2+3+...+10=55次而已。其实下面的代码加上换行符,就是常见的逐行递增1的代码啊*/ #include<stdio.h> #include <stdlib.h> #include <windows.h> int main() { int i,j=1; i=1; while(i<=10) { j=1; while(j<=i) { printf("OK");j++; } i++; } return 0; }
第九节 奔跑的小人
小人
O
<H>
I I
思路:小人作为一个整体,或者更逼真一点的是每个符号都一秒前进一步。
每个符号,都一秒前进一步。符号上,这里分成三段来看即可,即加入了这一,行的控制,用换行符。
第一秒,清屏,打印O,换行,打印身体,换行,打印脚;第二秒清屏,每行都打印一个空格,然后打印O,换行,打印身体,换行;第三秒清屏,每行都打印2个空格,然后打印O,换行,打印身体,换行。
所谓每行都打印x个空格——首先第一行打印2个空格,然后打印O,且换行,第二行打印2个空格,打印身体,换行;第三行打印2个空格,打印脚。【这样一步步小人才会横着走】
每行都打印两个空格又细分为先打印第一行的两个空格,换行,第二行打印两个空格,换行,第三行打印两个空格。打印1个空格也是换3次行。但是这样每次打完空格换完行后接下去才打印字母,就会导致小人是竖着移动的
点击查看初始构思代码
/*首先就会想到之前的H字母跑起来的代码,借鉴思路,然后发现只有小人的头在动,原来是只在第一行打了空格,然后慢慢debug去改进的。 这里要是能想明白的话,其实只需要将打印空格的while循环复制,分别放在`printf("<H>\n");`和`printf("I I\n");`前面即可。 我当时没想明白这个换行打空格的,所以有了下面竖着的小人。*/ int j=1;//第几列 int x=1; int time=1000; while(x<10) { system("cls"); x=1; while(x<j)//此循环控制打印空格的数目 {printf(" ");x++;} printf(" O \n"); printf("<H>\n"); printf("I I"); Sleep(time); j++; }
啊,我半小时实现了竖着走,这个横着走还是没思路。【这个当时还是因为只有头在动】
int j=1;//第几列 int x=1;//空格数目 int time=1000; while(x<10) { system("cls"); x=1; while(x<j)//此循环控制打印空格的数目 { printf(" \n");x++;} printf(" O \n"); while(x<j)//此循环控制打印空格的数目 { printf(" \n");x++;} printf("<H>\n"); while(x<j)//此循环控制打印空格的数目 { printf(" ");x++;} printf("I I"); Sleep(time); j++; }
啊,去WC的时候想到了,打印完非空字符不是换行了吗,接下来重复让x恢复初始状态,打印完空格就好。
/*想让小人跑得更快,调小time即可;想让小人跑得更远,那允许的空格数目(x)的限制值更大即可*/ #include<stdio.h> #include <stdlib.h> #include <windows.h> int main() { int i=1,j=1;//第几行、第几列 int x=1;//空格数目 int time=1000; while(x<10) { system("cls"); x=1; while(x<j)//此循环控制打印空格的数目 { printf(" ");x++;} printf(" O \n"); x=1; while(x<j)//此循环控制打印空格的数目 { printf(" ");x++;} printf("<H>\n"); x=1; while(x<j)//此循环控制打印空格的数目 { printf(" ");x++;} printf("I I"); Sleep(time); j++; } return 0; }
小人从右往左跑
思路:就是空格的打印数目反过来,从多到少。
首先第一行打印2个空格,然后打印O,且换行,第二行打印2个空格,打印身体,换行;第三行打印2个空格,打印脚。【这样一步步小人才会横着走】
#include<stdio.h> #include <stdlib.h> #include <windows.h> int main() { int i=1,j=0;//第几列,控制总的小人的数目 int x=10;//空格数目 int time=1000; while(j<=10)//这里不再是空格数目作为判断条件了,是因为还是之前的x>0的条件的话,小人走到左栏后一直在打印小人(因为此时x<(j==10)了,跳不出循环 { system("cls"); x=10; while(x-j>0)//此循环控制打印空格的数目,第一秒打印的空格是最多 { printf(" ");x--;}//后面空格逐次递减 printf(" O \n"); x=10; while(x-j>0)//此循环控制打印空格的数目,第一秒打印的空格是最多 { printf(" ");x--;}//后面空格逐次递减 printf("<H>\n"); x=10; while(x-j>0)//此循环控制打印空格的数目,第一秒打印的空格是最多 { printf(" ");x--;}//后面空格逐次递减 printf("I I"); Sleep(time); j++; } return 0; }
第十节 for隆重登场
题目:从1循环打印到10。
循环三要素:赋初值(进入循环),条件判断(什么时候退出循环),条件判断的变量的变化(一定会变,否则退不出循环)。
while写法:
int a=1;//赋初值 while(a<=10)//循环条件判断 {printf("%d",a);a++;}//变量的变化
for写法:更好的风格还是第一种
int a; for(a=1;a<=10;a++)//这里注意最后的变量变化语句无分号 {printf("%d",a);} //for循环对变量a的定义也可以写在括号里,且支持多个语句,用逗号分隔即可。 for (int a=1;a<=10;a++) {printf("%d",a);} //多个语句示例: for(int i=0, intj=0; i<10; i++,j++)//三要素任何一个要素或三个要素都能省略,但是分号要留着。
可见,因为while循环把三要素分开写,一旦漏写了某一个要素,while循环就无法正常运行,较容易忘记赋初值和变量变化。for循环就直接把三要素都写在一起,更加方便,只看括号内的内容便知道循环的初值、到什么时候退出、每次循环变量怎么变。
/*for循环写从1加到100:*/ int i,sum; sum=0; for(i=1;i<101;i++) {sum=sum+i;} /*for循环写打印1~100所有偶数*/ int i; for(i=2;i<=100;i=i+2) {if(i%2==0) printf("%d",i); } /*for循环打印输出1~100之间所有7的倍数或末尾含7的数/ int a; for(a=1;a<=100;a++) { if(a%7==0 || a%10==7) printf("%d",a); }
for循环比while循环要简洁很多,那为什么还要学习while循环呢?实际上,在一直循环次数的时候,for循环确实更好用,但不是所有情况下都是for更好,还是要看我们对于循环的需求。随着编程学习慢慢深入,会了解什么时候该用for循环,什么时候该用while循环。还有一种do-while循环。
for循环打印菱形
思路:类似while循环的,空格占位,记得换行。中间横的对角线有9个*,那么第一行前面有4个空格,第一行只有一个符号;第二行前面有3个空格,第二行有三个符号;第三行有5个符号,前面2个空格。
第一行,打4个空格,1个符号,换行;第二行,打3个空格,3个符号,换行;第三行,打2个空格,5个符号,换行;
int x,i,j;//空格数、行数、列数 for(i=1;i<=5;) { x=4;i=1; for(j=1;x>=j;x--)//控制打印的空格数 {printf(" "); } for(;i<=j+1;i=i+1)//每行打印j个符号 控制换行与符号打印 printf("*"); printf("\n"); j=j+1; } for(i=1;i<=4;i++) { x=1; for(;x<=i;x++)//控制打印的空格数,这是逐行递增 {printf(" "); } for(j=7;i<=j;i=i++)//每行打印j个符号 控制换行与符号打印 printf("*"); printf("\n"); j=j-2; }
奇了怪了,i++没有反应,但是i=i+1又可以??
debug:
打印了三行就停止了?你看打印完2行i就变成4了(变成4是在打印型号那里i++了几次),显然,用行数i和b的关系来控制星号的打印不妥,会影响到打印多少行,这个星号又是奇数,可以加一个变量代替不合适的i去给b计数,这样就不影响行数:
当然想用2i-1这种找序列和行数的关系表达式也可以。
发现问题:codeblocks里面for里面最后的i++,但是运行时i的值却不变,导致一直跳不出循环;而改成i=i+1就正常了,不知什么原因。
啊,我知道了,i++;
本身就是两步操作:i+1,然后赋值给i,等效于i=i+1;
你这个i=i++,它就是先使用了i的值,然后i+1(而i+1只是个式子,计算了变量加一后的值,但是i的值仍是原来的值),但是for循环那里只能执行一步操作,那这里就是使用i的值,所以i就一直不变了(书写错误啊)。
后面修正的完整程序:
#include<stdio.h> #include <stdlib.h> int main() { int x,i,j=1,b=1,a=1;//空格数、行数、列数、等差数列、计数变量 for(i=1;i<=5;i++ )//*号递增的循环打印 { x=4; a=1; for(;x>=j;x--)//控制打印的空格数 { printf(" "); } for(;a<=b;a=a+1)//每行打印j个符号 控制换行与符号打印 { printf("*"); } printf("\n"); j=j+1; b=b+2;//j控制的是列,列或行都不能跳,那就需要一个变量来控制奇数等差序列 } //经过上面,j=6,b=11,a=10这里重新赋值让j=4,b=7以方便下面操作 b=b-4;j=j-2; for(i=1;i<=5;i++ )//*号递减的循环打印 { x=4; a=1; for(;x<=j;x++)//控制打印的空格数,这里空格数逐渐增加 { printf(" "); } for(;a<=b;a=a+1)// { printf("*"); } printf("\n"); j=j+1; b-=2;//递减的奇数等差序列 } return 0; }
题目:用for循环打印一个九九乘法表
思路:两种形态的吧,一个是左对齐,一个是右对齐。
左对齐,
第一行只有1*1=1,只有一列
第二行,1*2=2,2*2=4,有两列
第三行,1*3=3,2*3=6,3*3=9,有三列
i,j是行数、列数,列数对应是个常量,代表了乘数;被乘数则随着行的变化而变化。所以列数在外循环,行数在内循环。
第一行,s=i*j;i=1,j=1
第二行,s=i*j;i=2,j=1 to 2
那么加上打印输出的条件if(i<=j)
即可:
条件变成if(i>=j)
则是这种:
第五章
程序就三种结构——顺序、分支和循环。
第3节
判断一个正整数是否为质数
质数:在大于1的自然数中,除了1和它本身以外不能被其它数所整除的数。性质:质数p的约数只有两个:1和p。
思路:因为1除任何数都可整除,则只需遍除到它本身,确定只能被它自己整除即可。
int a;
for(i=1;i<=a;i++)
{
if(a%i==0 && i!=a)
printf("%d是质数",a);
}
上述代码不对,从1开始,这样子所有数都是质数了。改成i从2开始,到a-1,如果在这个区间a还能被整除,就说明不是质数,这里则需要引入一个判断变量d,当满足条件的时候,i赋值给d,这样d的值改变就说明a不是质数,反之d保持等于1的初值不变,a就是质数了。
重新整理思路:一个数肯定是能被1合它本身整除的,这个不用再判断。如果它还能被其他的数整除,那它就不是质数;反之它是质数。在for循环里通过i++来依次判断能否整除,那么在[2,a-1]区间满足a%i==0
后,因为i的增加而输出是否是质数的结果来判断?这行不通。不如引入一个变量,去判断每次增1的i是否满足条件。
第一步,输入正整数a,d=1;
第二步,for(i=2;i<a,i++),循环体内:当a%i==0
的时候,i赋值给d,此时d的值不再是1。
第三步,for循环外,如果d不再是1,那么a是合数;如果d仍是1,说明a只有1和a这两个约数,a是质数。
int a,i,d=1; scanf("%d",&a); for(i=2;i<a;i++) { if(a%i==0 ) d=i; } if(d==1) printf("%d是质数",a); else printf("%d是合数",a);
而如果让d初值为0,后面若a%i==0
的时候,d++,则还实现约数个数的计算,参考书上代码:
int a,i,count=0; scanf("%d",&a); for(i=2;i<=a-1;i++) { if(a%i==0 ) count++; //printf("%d ",i);//若加上这句作为if下的组合语句,则可以输出a的约数的值。 } if(count==0) printf("%d是质数,有%d个约数",a,count); else printf("%d是合数,有%d个约数",a,count); /*或者这样写: if(count) printf("%d是合数,有%d个约数",a,count); else printf("%d是质数,有%d个约数",a,count); */
代码优化:因为本来就只需要判断输入的正整数是不是质数,不需要我们输出约数的个数、约数的数值,所以每当count的值从0加上1,即进入过一次if(a%i==0 )
后,就可以直接判断输出不是质数了,不需要继续验证存在其它的约数:
int a,i,count=0;
scanf("%d",&a);
for(i=2;i<=a-1;i++)
{
if(a%i==0 )
{
count++;
break;
}
}
if(count)
printf("%d是合数,有%d个约数",a,count);
else
printf("%d是质数,有%d个约数",a,count);
本来6有2、3这两个约数,但是程序执行到`6%2==0`后,就break跳出for循环了,不再判断6%2的结果。
最开始看到这个文章:https://blog.csdn.net/nanyu/article/details/6506454发现codeblocks可以从代码转换成N-S盒图:
- 首先选中要转换成盒图的代码,然后点击鼠标右键,找到Nassi Schbneidetman-Create diagram
- 就可以看到自动生成的盒图
- 点击codeblocks左上角的图标可以添加指令、判断、选择框等;如果想要编辑的话,鼠标停留在要编辑的文字处,等待鼠标从箭头变成框选输入文字那种符号,然后点击文字,就可以进入修改状态了。
那文章里说是可以N-S图和代码相互转换,但是我不知道如何将N-S图转成代码。
验证4~100内的所有偶数都可写成2个质数之和。
思路:首先表示4~100内的所有偶数,在一半的数内验证,因为如果前一半的数有一个符合的质数,那么后一半也肯定存在一个质数去满足(待验证数-前一半的符合质数=后一半的质数)
int counta=0,countb=0;
for(i=4;i<=100;i=i+2)
{
for(a=2;a<=i/2;a++)//在2~i/2内找到第一个质数a
{
for(k=2;k<=a-1;k++)//验证a是否为质数
{
if(a%k0)
counta++;
}
if(counta0)//若a是质数
{
b=k-a;
for(k=2;k<=b-1;k++)//验证b是否为质数
{
if(b%k0)
countb++;
}
if(countb0)//若b是质数
printf("%d=%d+%d ",i,a,b);
}
}
}
其它
条件操作符(三目运算符)`?:有时可以相当于if-else的简写。
do-while循环,至少执行一次,为什么呢?
因为它的执行过程是这样子的:
- 执行循环体里的内容
- 判断控制条件
- 控制条件为真,回到第一步
- 控制条件为假,跳过循环,执行循环体外下面的语句
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库