我决定了要成为海贼王便要为此而战,我必须变的更强。———路飞
阶段1目标:
基本了解C语言语法,站在全局,避去晦涩难懂,鲜明梳理C语言基本概念,为算法竞赛等计算机专业比赛铺好道路。传统功夫讲究点到为止,此阶段仅点明语法知识,后续阶段再进一步精进学习。
目录
分支与循环语句
C语言有3种结构:
- 顺序结构(一件事情从头做到尾)
- 选择结构(一件事情多种不同选择)
- 循环结构(一直在重复做一件事情)
1.分支语句
包括if语句,switch语句
1.1if语句
1.1.1if语句形式
if(表达式1){
语句1;
}
else if(表达式2){
语句2;
}
else{
语句3;
}
首先看if()语句,如果满足表达式1,则进入if(表达式1){语句1},....以此类推,注意,程序在if分支语句中只会选择进入一个if。
对比以下代码,可以看出,if(表达式)中的表达式不能连写,需要用 && 或者 | | 来表示
if(18 <= age < 30){
printf("青年\n");
}
//这是错误的代码段,不能连写表达式
说明:第二个程序会执行如下逻辑 —> 先从前往后算,18 <= 10,是非,用0表示,则0 < 30,是真,用1表示,则会打印出“青年”。
如果表达式的结果为真,则语句执行。
那么在C语言中如何表示真假? ——> 0表示假,非0表示
1.1.2if书写形式
当if语句想在同意表达式内执行多条代码的时候,要加大括号{ },这里的一对 { } 称为一个代码块;否则容易出现歧义,代码对比如下:
1.1.3if语句代码风格
参考如下代码,试说出运行结果?
#include<stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("哈哈\n");
else
printf("呵呵");
return 0;
}
——>结果为 空 ,在此程序中,我故意把else往前放,称为else悬空,营造和第一个if对其的假象,其实在if语句中,有条不成文的规定:else是和离他最近的if匹配的
另外,适当的添加{ }可以使代码逻辑更加清晰,即:上述代码等价于如下代码
由此稍稍浅谈C语言中的代码风格,养成良好的编码风格的重要性,例如:
//一些需要自己定义的变量,函数名一般需要直观的可以看出意义,例如:Add(10,20);
//自己定义变量名,一般小写单词之间用_连接,或者前一个单词第一个字母大写和后一个单词第一个字母大写,例如:
//char first_name[20];或者写作 char FirstName[20];
1.1.4if书写形式的对比
//代码1
if (condition) {
return x; }
return y;
//代码2
if(condition) {
return x; }
else
{
return y; }
//代码3
int num = 1;
if(num == 5) {
printf("hehe\n");
}
//代码4
int num = 1;
if(5 == num) {
printf("hehe\n");
解释代码1:
碰到 return 代码执行结束,例如:
说明:if(1){ } 成立,进入表达式,打印出“呵呵”,之后碰到return 3程序结束,不再执行打印“哈哈”,return 是程序结束的标志
利用VS2019监视功能,ret接收test()的返回值,得出返回值为3
同理:if(0){ },返回值是-3
一般采用代码二,逻辑更加清晰
解释代码三,四:首先对比我给出的以下代码
//对比第一个第二个代码
//第一个
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int num = 3;
if (num == 5) {
printf("哈哈\n");
}
return 0;
}
//第二个
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int num = 3;
if (num = 5) {
printf("哈哈\n");
}
return 0;
}
注意:
没错,仅仅在if()判断里=数量不一样,首先第一段代码里判断运算if (num == 5),判断num和5是否相等,由程序知道num等于3,不等于5,所以不打印,为空,这是我们的本意;
可是当我们疏忽,漏泄一个等号,写成赋值运算if (num = 5),此时程序不会报错,会正常编译,进入分支语句,打印出“哈哈”,然而这不是我们的本意。
而我们不想让这种错误发生,想出现这种情况时编译器会告诉我们从而避免错误,此时代码四的写法就应运而生了...
当写成赋值运算的时候,编译器会报错,从而提醒我们,因为赋值运算中变量不能赋值给常量
好的代码素养:
- 代码块能用尽量用,逻辑会更加清晰。
- 常量和变量比较相等的时候,提倡代码应该把常量放在左边更好一点,这样可以把本想写==判断语句而写成=赋值语句的情况避免掉。
一般程序中我们会采用代码二和代码四
1.2switch语句
switch语句也是一种分支语句。 常常用于多分支的情况。
输入 1 ,输出星期一输入 2 ,输出星期二输入 3 ,输出星期三输入 4 ,输出星期四输入 5 ,输出星期五输入 6 ,输出星期六输入 7 ,输出星期七
说明:当然我们可以采用if语句 if...else if ...else if来描述这段代码,但是这种代码略显冗余,这时候 switch语句就应运而生了。
1.2.1switch语句的形式
switch ( 整型表达式 ){语句项;}
//是一些case语句 :
//如下:case 整形常量表达式 :语句 ;
case 语句是入口,break是出口
对比以下代码段:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
//代码显得冗长
/*int day = 0;
scanf("%d", &day);
if (1 == day) {
printf("星期一\n");
}
else if (2 == day) {
printf("星期二\n");
}
else if (3 == day) {
printf("星期三\n");
}
else if (4 == day) {
printf("星期四\n");
}
else if (5 == day) {
printf("星期五\n");
}
else if (6 == day) {
printf("星期六\n");
}
else if (7 == day) {
printf("星期七\n");
}*/
//——>采用switch语句
int day = 0;
scanf("%d", &day);
switch (day) {
case 1:
printf("星期一\n");
case 2:
printf("星期二\n");
case 3:
printf("星期三\n");
case 4:
printf("星期四\n");
case 5:
printf("星期五\n");
case 6:
printf("星期六\n");
case 7:
printf("星期七\n");
}
}
代码解释:由以上代码片段,我们可以看出case 数字:——>仅仅是程序的入口,所以在输入4的时候,不单单输出“星期四”,而把case 4:往后的都打印出来了,那么如何达到我们的想要的输出效果呢?
这时就要我们引入关键字 break ,代码如下:
1.2.3default子句
思考一点,如果表达的值与所有的 case 标签的值都不匹配怎么办? 其实也没什么,程序并不会终止也不会报错,就是所有的语句都被跳过了而已。
然而我们不想让他直接跳过程序了,我们想让他输入错误的时候有个提示,告诉他输入错误,此时我们的defult语句就应运而生了
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int day = 0;
scanf("%d", &day);
switch (day) {
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期七\n");
break;
default:
printf("输入数据有误,请输入1~7的数字:");
}
}
注意:
当 switch表达式的值与所有case 标签的值都不匹配时,这个 default 子句后面的语句才会执行,所以,每个switch 语句中只能出现一条default子句 。它可以出现在语句列表的任何位置。
总结:
- switch(整型表达式),注意是整型表达式 int,见代码图1:
- case后面记得先加空格,再写整型常量表达式
- case后面必须加的是整型常量表达式,注意是整型 常量表达式,见代码图2:
- 整型常量表达式不单单指的是 整型数字1,2,3...还有可以转化为ASCII码值的字符例如‘a’,'b'...,见图3:
- 在switch语句中,break是一个case的出口,没有break,我们无法直接实现分支,搭配break使用才能实现真正的分支。
- 当然,程序也不是有入口case就一定要有出口break,当几个连续发生的内容时,可以只有入口,没有出口,见图4:
- switch语句中运行程序最后的break可以加也可以不加,好的代码习惯应该加上,因为这样方便以后继续添加新的代码段,同见图4:
- 当 switch表达式的值与所有case标签的值都不匹配时,这个default子句后面的语句才会执行,所以,每个switch语句中只能出现一条default子句,见图5:
- default字句可以出现在语句列表的任何位置,见图6:
图1
图2
图3
图4
图5
图6
注意点:switch语句可以嵌套使用
2.循环语句
1.while
2.for
3.do...while
好,由分支语句的if语句我们了解到,如下代码段:
if(条件){
语句;
}
——>当条件满足的情况下, if 语句后的语句执行,否则不执行。但是这个语句只会执行一次。但是我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次。那我们怎么做呢? C 语言中给我们引入了: while 语句,可以实现循环
2.1while语句
while(表达式){循环语句;}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int i = 1;
while (i <= 10) {
printf("%d\n",i);
i++;
}
return 0;
}
此代码实现从1打印到10
那么下面我们来看一下while语句的执行流程图:
解释:while语句中会包含break和continue两个关键字,在特殊情况下才使用,那么下面我们来浅谈一下这两个关键字:
思考如下程序,会打印什么结果呢?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int i = 1;
while (i <= 10)
{
if (5 == i)
break;
printf("%d\n", i);
i++;
}
return 0;
}
说明:while里的break语句,满足if直接跳出while循环,即:无论是在while循环语句还是case分支语句,只要执行break都会终止后面的所有程序,跳出循环。
即:在while 中的break是用于 永久 终止循环的。
2.2.2while语句中的continue
思考如下程序,会打印什么结果呢?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int i = 1;
while (i <= 10)
{
if (5 == i)
continue;
printf("%d\n", i);
i++;
}
return 0;
}
说明:continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分
解释:程序会输出1~4,当i=5时,此程序会执行continue,执行continue跳转到while(i<=10)位置,由于未执行i++,所以i恒为5,所以输出1~4以后程序是死循环,才会有光标跳动,
下面我用VS2019调试功能来监视i的变换情况:
说明:continue是用于跳过本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分
同理,思考一下代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int i = 1;
while (i <= 10)
{
i++;
if (5 == i)
continue;
printf("%d\n", i);
}
return 0;
}
这里只给出结果:2,3,4,6,7,8,9,10 注意没有打印5
延伸一下:getchar()和putchar(),首先给出如下代码:
解释说明:
getchar()——>从键盘获取一个字符
putchar()——>把字符打印在屏幕上
//其实如下代码等价
//1段
int ch = 0;
ch = getchar();
putchar(ch);
//
//2段
int ch = 0;
scanf("%s",&ch);
printf("%s",ch);
//
那么我们来思考一下如下代码段:
//代码什么意思?
//代码1
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
//代码2
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
return 0;
}
首先看代码1,我们得出如下运行结果:
是输入EOF才能让它停下来吗?好,我们试试看
可见输入EOF程序控制台还是没有让我们停止输入,那么我们不禁会想,程序中的(ch = getchar()) != EOF,是什么意思呢?
给出解释:EOF——end of file,是文件结束的标志,getchar()读取失败的时候就会返回EOF,而我们会在不想输入的时候按下Ctrl+Z,见下图:
代码解释:当我们按下Ctrl+Z的时候,getchar()返回EOF,那么程序中的(ch = getchar()) != EOF ——> EOF != EOF是假,即:不满足while循环条件,跳出循环,程序结束
你是不是以为这样代码1就结束了?不!难道你就没有疑问为什么 getchar()获取字符,为啥要用int ch = 0这种整型来定义呢?不应该是用char 类型来定义吗?我写错了??
哈哈哈,听我娓娓道来叭~
用int整型类型定义而不用char类型定义原因主要有两点:
- 返回的是字符,字符本质上也是ASCII码值,是整数,用int类型没毛病。
- getchar函数不仅仅返回正常字符,还会返回EOF——而EOF本质上是-1(见下图),所以放在整型变量中,而不能放在char类型中。
下图说明EOF的本质定义为啥是-1:
1.选中EOF,鼠标右击,弹出窗口后点击转到定义。
2.我们可以看到EOF定义,就是-1。
那么我们的getchar()有啥用处吗?在啥场景下使用呢?下面给出一段代码,请思考我们会在输入密码出现什么结果?自己输入Y/N然后程序给你输出“确认成功!” 或 “确认失败!”吗??:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int ch = 0;
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", &password);
printf("请确认密码(Y/N):");
ch = getchar();
if (ch == 'Y')
{
printf("确认成功!");
}
else
{
printf("确认失败!");
}
return 0;
}
事与愿违,我们看出,输入一串密码后并没有给我任何输入Y/N的机会,直接跳出了“ 确认失败! ”,为啥?
来来来,下面我们再再延伸普及一个知识——>输入缓冲区
来来来,说道说道为啥一点输入的机会都不给就给我们输出了“确认失败!”字样:
在此程序中,scanf输入12345,我们敲下回车\n,而此时会把12345传递给password,输入缓冲区只剩下我们敲的回车键\n,所以getchar捕获到了\n,发现不符合if(ch = Y),就默认执行了else语句,直接输出了“确认失败!”
注意点:
那么我们如何让程序老老实实运行呢?我们由上述可以得出,getchar函数可以提取输入缓冲区的字符,好!那么我们就利用一下getchar函数,让它替我们把\n先拿走,
那么下一个getchar就啥字符也提取不到,就会等待用户输入Y/N.
你以为这就完事大吉,完事了??不不不,我再给出刚刚同样的代码,看看我这样输入是否还是能正常运行? ——> 12345 abcd:
输入12345 abcd后,直接就跳转输出“ 确认失败!”,为什么呢?下面先普及一个知识点:
- 使用字符说明符%s时,一旦遇到空格,读取工作将终止
- getchar只能读取一个字符
看下面输入缓冲区执行流程:
解释说明:
键盘输入12345 abcd\n,scanf中的%s只能读取到不是空格的字符串,遇见空格就会读取停止,所以把12345读取到,拿给password
此时缓冲区还剩 abcd,第一个getchar读取一个字符——>空格,第二个getchar读取一个个字符——>a,然而a不等于Y,所以执行else语句,直接打印出“ 确认失败!”
所以我们得出结论,遇见空格这种在程序中插入getchar函数的方式去清理字符,清理不干净,没有普适性不可取,我们做出如下修改:
//清理\n
//getchar();不可取
while ((ch = getchar()) != '\n')
{
;
}
解释说明:
我们发现直接用getchar清理字符清理不干净,我们就把代码改为如上图所示,先把键盘输入的12345拿给scanf,然后 abcd,传递给while循环,例如:
空格 != ' \n ',则进入while空循环内,把缓冲区的空格拿走清理掉了
a != ' \n ',则进入while空循环内,把缓冲区的a拿走清理掉了
...
如此循环执行,直到遇见\n,才不执行while循环,进入下一句程序,打印出“ 请确认密码(Y/N):”
上个问题基本结束讲解,再看代码二,会打印出什么呢?
//代码2
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
return 0;
}
猜想:会打印ASCII码表里字符‘ 0 ’到‘ 9 ’的字符,下面附上ASCII表
解释说明:???
2.2 for语句
for(表达式1;表达式2;表达式3)
循环语句;
好,那么我们说说为啥我们在有了while循环的基础上,还要引入for循环的原因,先看下面代码:
若是需要在while中补充内容,需要空出那么多空,则while显得不太灵活,才引出for循环
那么写成for循环,代码如下:
对应的,for(表达式1;表达式2;表达式3){ 循环体 } 中
表达式1:初始化
表达式2:判断
表达式3:调整
for执行流程图
好,看了流程图之后我们再讲讲for循环里的break语句和continue语句
2.2.2for循环中的break语句
首先给出如下代码,试试能否猜出打印结果:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 1;
for (i = 1; i <= 10; i++)
{
if (5 == i)
break;
printf("%d ", i);
}
return 0;
}
没错,打印结果是1 2 3 4,那么break语句在for循环里是什么作用呢?
break语句在for循环中的作用依然和while循环里的作用一样,都是用来终止程序
即:一碰到break语句,就跳出循环
2.2.3for循环中的continue语句
那么由break代码,可以知道break是用来终止循环的,那么continue语句是用来干什么的呢?
我们先看下列代码,试说出他的打印结果:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 1;
for (i = 1; i <= 10; i++)
{
if (5 == i)
continue;
printf("%d ", i);
}
return 0;
}
没错,打印结果是1 2 3 4 6 7 8 9 10,那么continue语句在for循环里是什么作用呢?
注意:在for循环中的continue语句不同于while循环中的continue语句,那么我们来对比一下他们的不同作用:
1.在for循环中,continue跳过continue后面的代码,去了循环中的调试部分,调整循环变量,不容易造成死循环。
2.在while循环中,continue跳过continue后面的代码,去了循环中的判断部分,容易死循环。
//代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 1;
for (i = 1; i <= 10; i++)
{
if (i = 5)
printf("%d ", i);
}
return 0;
}
//代码2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)//等价于for (i = 0; i <= 9; i++)
{
scanf("%d", &arr[i]);
}
for (i = 0; i < 10; i++)//等价于for (i = 0; i <= 9; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
图1:
图2:
解释说明:
代码1:用赋值符号=,则if表达式非0,恒为真,所以一直死循环打印5代码2:i < 10意思等价于i <= 9,但是i < 10有意义,表示0~9这十个元素,因此不采用i <= 9这种写法
看下列程序会打印什么?:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//省略问题
//1.允许都省略
//2.判断部分只要省略了,就意味着判断恒为“真”————>程序会死循环
int i = 0;
for (; ; ) //初始化,判断,调试,三个部分都省略了
{
printf("哈哈哈\n");
}
return 0;
}
解释说明:
结果是死循环打印“哈哈哈”
总结:省略问题
1.允许都省略
2.判断部分只要省略了,就意味着判断恒为“真”————>程序会死循环
//代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for (i = 0;i < 10 ;i++ )
{
for (j = 0; j < 10; j++)
{
printf("哈哈哈\n");
}
}
return 0;
}
//代码2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for (;i < 10 ;i++ )
{
for (; j < 10; j++)
{
printf("哈哈哈\n");
}
}
return 0;
}
先给结果:代码1,结果是打印出100行“哈哈哈”;代码2,结果是打印10行“哈哈哈”
解释说明:代码1:i = 0满足i < 10,进入第一个for循环,接着进入第二个for循环,j = 0,满足j < 10,执行打印第一行“哈哈哈”;接着j++,即j = 1,继续执行第二个for循环,j = 1,满足j < 10,打印第二行“哈哈哈”,...,直到j = 10的时候,退出第二个for循环,回到第一个for循环,以上是i = 0的时候打印情况,打印了十行“哈哈哈”; 接着i++,即i = 1,i = 1满足i < 10,进入第一个for循环内,接着又进入第二个for循环,继续执行刚才的操作...代码2:第一个for循环,初始化为空,默认上面的int i = 0这个初始化,i = 0 满足i < 10,进入第一个for循环,接着进入第二个for循环,同理,第二个for循环初始化也默认是int j = 0,j = 0 满足j < 10,打印第一行“哈哈哈”;接着j++,即使j = 1,继续执行第二个for循环,j = 1,满足j < 10,打印第二行“哈哈哈”,...,直到 j = 10的时候,退出第二个for循环,注意此时 j = 10 ,退出第二个for循环,以上是i = 0的时候打印情况,打印了十行“哈哈哈”; 接着i++,即i = 1,i = 1满足i < 10,进入第一个for循环内,接着又进入第二个for循环,注意,此时没有初始化部分,即: 没有j = 0部分让j重置,则j还是保持上一次循环的j = 10,即不在满足进入第二个for循环的条件,仅输出10行“哈哈哈”
2.3do...while循环
do{循环语句;}while(表达式);
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
do
{
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
do...while循环执行流程
2.3.2do...while循环里的break语句
先看一段代码,试说出执行结果:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
do
{
if (5 == i)
break;
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
没错,和while循环和for循环一样,结果会打印1 2 3 4,此处不带冗述break语句的语法,详细见while循环和for循环
2.3.3do...while循环里的continue语句
先看一段代码,试说出执行结果:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
do
{
if (5 == i)
continue;
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
是的,看完do...while的执行流程图再解决这个问题就知道,do...while循环里的continue语句和while循环里的continue语句一样,遇见了会发生死循环,如下图,光标闪烁
2.3.4do...while语句的特点
循环至少执行一次,使用的场景有限,所以不是经常使用。
哦吼,是不是跃跃欲试呢?下面选择合适的循环语句来做做练习题叭~~
//第一题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 1;
int b = 1;//阶乘
int n = 0;
scanf("%d", &n);
for (a = 1; a <= n; a++)
{
b *= a;
}
printf("!n = %d", b);
return 0;
}
第二题
//第二题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 1;
int b = 1;//阶乘
int sum = 0;
for (a = 1; a <= 10; a++)
{
b *= a;
sum += b;
}
printf("1! + 2! + 3! +4!...+10! = %d", sum);
return 0;
}
那么针对第二题,我们来讨论一个写法,是否可以对第一题代码进行改造,暴力解呢?
即:用第一题的方法计算阶乘,再改造一下,求出每个数字的阶乘的和
我们发现,这样稍稍修改代码好像说的过去,来运行试试,看看行不行?
(皮一下~.jpg)由于1!+2!+3!+...+10!数字较大,我们假设这个代码还需要进一步修缮,那么怎么修改呢?————不用着急,可以用调试功能
(程序是可以运行的,但是结果是错误的,这就叫做运行时错误,这个时候用调试功能,一步一步执行代码,观察代码是不是按照我们的期望走的,如果发现代码不是按照期望走的,bug就出现了!)
假设代码是从1!+2!+3!,那么程序会给我们正确结果9吗?
可见程序并没有给出正确结果,那么需要修改哪里呢?单步调试一下便知
经过调试,发现当n=3时出现问题,算出b是12,应该是6才对,那么问题出现在哪呢?
其实,是因为上一次算出的b累计的效果,那么我们把b每次初始化即可
第三题
//第三题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//0~9
int arr[10] = { 1,2,3,4,6,12,16,18,23,90 };
//只知道给了有序数组arr[],不知道给了多少个,也不知道给了什么数的情况下
//想实现在有序数组中,找数字6,找到则输出下标,找不到输出“找不到”
//求数据的元素个数
// 40 / 4
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int k = 6;
for (i = 0; i < sz; i++)
{
if (arr[i] == k)
{
printf("找到了,下标是:%d", i);
break;
}
}
if (i == sz)
{
printf("找不到\n");
}
return 0;
}
技巧:求数据的元素个数(数组元素字节数/每个元素所占字节数)
// 40 / 4
int sz = sizeof(arr) / sizeof(arr[0]);
是的,我们这样可以找的数组中符合条件的数字下标,但是效率太低,需要从前往后逐一查找
由此我们引出一种算法——> 折半查找(二分法)
折半查找思想
折半查找,效率更高
//第三题优化(折半查找)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//0~9
int arr[10] = { 1,2,3,4,6,12,16,18,23,90 };
//只知道给了有序数组arr[],不知道给了多少个,也不知道给了什么数的情况下
//想实现在有序数组中,找数字6,找到则输出下标,找不到输出“找不到”
//求数据的元素个数
// 40 / 4
int sz = sizeof(arr) / sizeof(arr[0]);
int k = 6;//要找的数字
int left = 0;
int right = sz - 1;
int mid = (left + right) / 2;
while (left <= right)
{
if (arr[mid] > k)
{
right = mid - 1;
}
else if (arr[mid] < k)
{
left = mid + 1;
}
else
{
printf("找到了,下标是%d\n", mid);
break;
}
}
if (left > right)
{
printf("找不\n");
}
return 0;
}
第四题
题目先给出解释,编写代码,演示多个字符从两端移动,向中间汇聚。什么意思呢?
例如:我们要实现“hello world!!!!!”则按照题目意思应该变为如下打印过程:
//hello world!!!!!!
//#################
//h###############!
//he#############!!
//hel###########!!!
//hell#########!!!!
// ...
//hello world!!!!!!
那么代码如何实现呢?
//第四题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
int main()
{
//hello world!!!!!!
//#################
//h###############!
//he#############!!
//hel###########!!!
//hell#########!!!!
// ...
//hello world!!!!!!
char arr1[] = { "hello world!!!!!!" };
char arr2[] = { "#################" };
int len = strlen(arr1);//需要自行添加头文件——>#include<string.h>
int left = 0;
int right = len - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
left++;
right--;
}
return 0;
}
打印效果如下:
技巧:计算字符串的长度代码
int len = strlen(arr1);//arr1是数组名
注意点:strlen计算字符串长度,需要自行添加头文件#include<string.h>
那么我们想看看打印效果一行一行停顿输出怎么实现呢?
//第四题优化(一行一行输出)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
//hello world!!!!!!
//#################
//h###############!
//he#############!!
//hel###########!!!
//hell#########!!!!
// ...
//hello world!!!!!!
char arr1[] = { "hello world!!!!!!" };
char arr2[] = { "#################" };
int len = strlen(arr1);//需要自行添加头文件——>#include<string.h>
int left = 0;
int right = len - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//1000毫秒——>1s
left++;
right--;
}
return 0;
}
可见我们就增加了两行代码:
Sleep(1000);//1000毫秒——>1s
不要忘记增加这一句需要自行添加头文件#include<windows.h>
下面我们来看实现效果
那我们如果想一行打印以后清理上一行再打印下一行怎么实现呢?
system("cls"); ——>清理干净
注意:需要自行添加头文件#include<stdlib.h>
实现效果代码如下:
//第四题优化(打印一行清理一行)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<windows.h>
#include<stdlib.h>
int main()
{
//hello world!!!!!!
//#################
//h###############!
//he#############!!
//hel###########!!!
//hell#########!!!!
// ...
//hello world!!!!!!
char arr1[] = { "hello world!!!!!!" };
char arr2[] = { "#################" };
int len = strlen(arr1);//需要自行添加头文件——>#include<string.h>
int left = 0;
int right = len - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//1000毫秒——>1s
system("cls");
left++;
right--;
}
return 0;
}
第五题
自己运行一下看看如下代码是否正确?
//第五题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
char password[20] = { 0 };
//假设密码是 abcdef
for (i = 0; i < 3; i++)
{
printf("请输入密码:\n");
scanf("%s",&password);
if (password == "abcdef")
{
printf("密码输入正确!\n");
break;
}
else
{
printf("密码输入错误!!");
}
}
if (i == 3)
{
printf("三次密码输入失败,退出程序!!");
}
return 0;
}
我们运行后发现,如下代码并不正确,输入abcdef显示输入错误,什么原因呢??
是因为,两个字符串比较,不能用==表示,需要用另一个函数表示:
strcmp(password,"abcdef") == 0
注意:需要自行添加头文件#include<string.h>
第五题正确代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
int main()
{
int i = 0;
char password[20] = { 0 };
//假设密码是 abcdef
for (i = 0; i < 3; i++)
{
printf("请输入密码:\n");
scanf("%s",&password);
if (strcmp(password,"abcdef") == 0)
{
printf("密码输入正确!\n");
break;
}
else
{
printf("密码输入错误!!");
}
}
if (i == 3)
{
printf("三次密码输入失败,退出程序!!");
}
return 0;
}
3.goto语句
C 语言中提供了可以在一个函数中 随意跳转的 goto语句 ,从理论上 goto 语句是没有必要的,实践中没有 goto 语句也可以很容易的写出代码。 那么下面我们来看看goto语句如何应用的,再去分析何时用得到goto语句。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
again:
printf("哈哈哈\n");
goto again;
return 0;
}
结果会产生死循环,那么我们什么时候需要goto语句呢?
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)
// 处理错误情况
如上述代码段,我们想要跳出多层深入嵌套循环,怎么办呢?用break?不,break只能跳出一层循环,那怎么办呢?——> 这个时候我们就采用goto语句跳转
下面我们来点刺激的——>关机程序
要求,程序运行起来就倒计时关机,1分钟内,输入:我是猪,就取消关机
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60");
again:
printf("请注意,你的电脑在1分钟内将会关机,如果输入:我是猪,将会取消关机\n");
scanf("%s", &input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
那么先来普及一个知识,再来解释代码叭~~
我们借用windows系统的命令提示符,来完成关机的代码
百度百科解释:
windows系统的命令提示符:Windows 命令提示符(即 cmd)是 Windows 系统的一种命令行操作工具,用户可以通过输入命令来完成各种各样的系统或程序操作。 虽然很多操作都可以通过图形程序完成,但也有非他不可的情况存在。 因此了解一些日常可能用到的简单操作也是很必要的。 ————>打开方式:
按下快捷键 Win + R 打开运行,输入 cmd 回车。
按住Win+R,出现如下界面,然后输入cmd回车
之后再输入shutdown -s -t 60回车
之后按shutdown -a,取消关机
这就是windows系统的命令提示符,其实goto语句在这种不是多个循环中还可以改成while循环,用break跳出,代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60");
while (1)
{
printf("请注意,你的电脑在1分钟内将会关机,如果输入:我是猪,将会取消关机\n");
scanf("%s", &input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
break;
}
}
return 0;
}
—— —— —— —— —— —— —— ~~~我是正文分割线~~~ —— —— —— —— —— —— ——
恭喜你,坚持住,即将变秃变强,记得及时巩固复习哟~~~~