C语言初阶秘籍2

Hello,大家好!!!这里是小周为您带来的呕心沥血之作------C语言秘籍!!

C语言秘籍分为初阶和高阶两部!!跟着小周学定会让你C语言功力大成,称霸武林,话不多说,我们接着上回继续开讲!!!!!

二、分支语句和循环语句

从今天开始我将带领大家深入学习每一个知识点

分支语句

  • if

  • switch

循环语句

  • while

  • for

  • do while

goto语句

1、什么是语句?

在C语言中,语句可分为以下五类:

  1. 表达式语句

  2. 函数调用语句

  3. 控制语句

  4. 复合语句

  5. 空语句

    int main()
    {
        3 + 5;//表达式语句
        printf("hehe");//函数调用语句
        int i = 1;
        for (i = 1; i < 100; i++)
        {
            //复合语句,放在{      }中的这些语句
            printf("%d ", i);
            printf("%d ", i+1);
            printf("%d ", i+2);
        }
            ;//空语句
        return 0;
    }

本章后面介绍的是控制语句。

控制语句用于控制程序的执行流程,以实现程序的各种结构方式

它们由特定的语句定义符组成,C语言有九种控制语句。

可分成以下三类:

  1. 条件判断语句也叫分支语句:if语句、switch语句;

  2. 循环执行语句:do while语句、while语句、for语句;

  3. 转向语句:break语句、goto语句、continue语句、return语句。

2、分支语句(选择结构)

漫漫人生路,我们总是时时刻刻在做选择!!

如果你好好学习,校招时拿一个好offer,走上人生巅峰,迎娶白富美

如果你不学习,毕业等于失业,回家种地

2.1 if语句
2.1.1 if语句的语法结构
//1、
if(表达式)
    语句;
//2、
if(表达式)
    语句1;
else
    语句2;
//3、
if(表达式)
    语句1;
else if(表达式)
    语句2;
else
    语句3;
2.1.2 代码讲解

情景再现:小于18岁请在屏幕上打印未成年,大家想一想看看代码怎么写

公布答案:

int main()
{
    int age = 0;
    scanf("%d", &age);
    if (age < 18)
    {
        printf("未成年\n");
    }
}

情景再现:小于18岁请在屏幕上打印未成年,否则就打印成年,大家再想一想看看代码怎么写

公布答案:

int main()
{
    int age = 0;
    scanf("%d", &age);
    if (age < 18)
    {
        printf("未成年\n");
    }
    else
    {
        printf("成年\n");
    }
}

我们规定18岁以下是少年,18-30岁是青年,30-50岁是中年,50-80岁是老年,80岁以上是老寿星大家,再想一想看看代码怎么写

公布答案:

int main()
{
    int age = 0;
    scanf("%d", &age);
    if (age < 18)
    {
        printf("少年\n");
    }
    else if (age >= 18 && age < 30)
    {
        printf("青年\n");
    }
    else if (age >= 30 && age < 50)
    {
        printf("中年\n");
    }
    else if (age >= 50 && age < 80)
    {
        printf("老年\n");
    }
    else
    {
        printf("老寿星\n");
    }
}

在这里大家看,代码逻辑清晰,有很好的可读性,也可以这么书写

if (age < 18)
        printf("青少年\n");
else
    if (age >= 18 && age <= 30)
        printf("青年\n");
    else if (age >= 31 && age <= 50)
        printf("中年\n");
    else if (age >= 51 && age <= 80)
        printf("中老年\n");
    else if (age >= 81 && age <= 100)
        printf("老年\n");
    else
        printf("老寿星\n");

在这里再带大家回忆一下,如果表达式的结果为真,则语句执行。在C语言中如何表示真假?

0表示假,非0表示真。

那我们说日常工作的场景肯定不能像例子一样简单,肯定有多个语句构成,来实现对应的功能

如果条件成立,要执行多条语句,我们应该使用代码块

#include <stdio.h>
int main()
{
    if (表达式)
    {
        语句列表1;
    }
    else
    {
        语句列表2;
    }
    return 0;
}

这里的一对 {         } 就是一个代码块

2.1.3 悬空else

这里考大家一道,看看各位能不能发现其中的奥妙

int main()
{
    int a = 0;
    int b = 2;
    if(a == 1)
        if(b == 2)
            printf("hehe\n");
    else
        printf("haha\n");
    return 0;
}

请问这个代码输出什么?仔细想

我们发现什么都没有打印,那这是为什么呢?其实有细心的小伙伴已经发现了,图片中的代码在格式上和上面代码块中的代码有一些不同,我们在看代码块的代码时会发现,我们主观的认为else是和第一个if匹配的,结果应该是打印haha,其实不是这样的

else的匹配:else是和它离的最近的if匹配的。

我们现在的编译器比较智能,比较人性化,当代码粘贴到编译器时,会自动匹配,所以很容易就发现输出什么

如果想实现打印haha的结果不妨这么做

#include <stdio.h>
int main()
{
    int a = 0;
    int b = 2;
    if(a == 1)
   {
        if(b == 2)
       {
            printf("hehe\n");
       }
   }
    else
   {
         printf("haha\n");
   }       
    return 0;
}

适当的使用{         }可以使代码的逻辑更加清楚,代码风格很重要

2.1.4 if书写形式的对比

我们先来看这两组代码

//代码1
if (condition) {
    return x;
}
return y;
//代码2
if(condition)
{
    return x;
}
else
{
    return y;
}

其实这两组代码效果是一样的,但是代码2的逻辑更加清晰,不容易出错

接着看这两组

//代码3
int num = 1;
if(num == 5)
{
    printf("hehe\n");
}
//代码4
int num = 1;
if(5 == num)
{
    printf("hehe\n");
}

差别就在判断条件上,那小周个人习惯是代码3的方式,但其实代码四的逻辑更加清晰,不容易出错,那为什么呢?我们发现在写代码时,难免会出现各种各样的错误

因为我们少写了一个=,就造成了这种结果,程序死循环了,如果我们按照代码4来写的话,就可以及时发现这种错误

我们就能及时发先错误并改正

2.1.5 练习

这里有两道小题拿来给大家练练手,大家先自己下去尝试写一写,看看能不能实现对应的功能,有不明白的问题可以私信我啊

1、判断一个数是否是奇数

int main()
{
    int n = 0;
    //输入
    scanf("%d", &n);
    if (n % 2 == 1)
    {
        printf("是奇数\n");
    }
    else
    {
        printf("不是奇数\n");
    }
    return 0;
}

2、输出1-100之间的奇数

//方法1
int main()
{
    int i = 1;
    while (i <= 100)
    {
        //判断i是奇数的话,就打印i
        if(i % 2 == 1)//%2等于1的话就是奇数
            printf("%d ", i);
        ++i;
    }
    return 0;
}
//方法2
//因为奇数是1 3 5 7 9......所以每次在原有基础上+2,就省去了方法一的判断和循环
int main()
{
    int i = 1;
    while (i <= 100)
    {
        printf("%d ", i);
        i = i + 2;
    }
    return 0;
}
2.2 switch语句

switch语句也是一种分支语句,常常用于多分支的情况

比如:

输入1,输出星期一

输入2,输出星期二

输入3,输出星期三

输入4,输出星期四

输入5,输出星期五

输入6,输出星期六

输入7,输出星期日

int main()
{
    int day = 0;
    //输入
    scanf("%d", &day);
    if (1 == day)
        printf("星期1\n");
    else if (2 == day)
        printf("星期2\n");
    else if (3 == day)
        printf("星期3\n");
    else if (4 == day)
        printf("星期4\n");
    else if (5 == day)
        printf("星期5\n");
    else if (6 == day)
        printf("星期6\n");
    else if (7 == day)
        printf("星期天\n");
    else
        printf("输入错误\n");
​
    return 0;
}

我们发现上述代码有点麻烦,那我们不想用if else结构,那我们就得有不一样的语法形式,这就是switch 语句。

switch(整型表达式)
{
    语句项;
}

语句项是什么呢?

//是一些case语句:
//如下:
case 整形常量表达式:
    语句;

我们发现,switch语句有一些特殊的规定,switch后面的()中必须是整型表达式,case后面也得接整形常量表达式

那介绍完switch的用法,这个代码该怎么写呢?

int main()
{
    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");
    }
    return 0;
}

我们发现怎么输入2,把剩下的都打印出来了,并没有按照要求实现分支的功能啊!!!

2.2.1 在switch语句中的break

其实在switch语句中,我们没办法直接实现分支,搭配break使用才能实现真正的分支

int main()
{
    int day = 0;
    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;
   }
    return 0;
}

这样就能正确实现分支的功能了,上述代码的执行逻辑是,我在键盘上输入2,然后与case后面的值进行匹配,对应打印星期二,遇到break,则直接跳出循环,我们调试看一下

再按一下F10

我们发现遇到break,直接跳出了循环

有时候我们的需求变了:

  1. 输入1-5,输出的是“weekday”;

  2. 输入6-7,输出“weekend”

那这时就应该这么书写代码

因为周一至周五都是weekday,所以我们只需在这种情况的最后一个case中加入break即可

break语句的实际效果是把语句列表划分为不同的分支部分

编程好习惯

在最后一个 case 语句的后面加上一条 break语句

(之所以这么写是可以避免出现在以前的最后一个 case 语句后面忘了添加 break语句)

2.2.2 default子句

如果表达的值与所有的case标签的值都不匹配怎么办?其实也没什么,结果就是所有的语句都被跳过而已。

程序并不会终止,也不会报错,因为这种情况在C语言中并不认为是个错误。

但是,如果你并不想忽略那些不匹配所有标签的表达式的值时该怎么办呢?

你可以在语句列表中增加一条default子句,把下面的标签

default:

写在任何一个 case 标签可以出现的位置。

当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。

所以,每个switch语句中只能出现一条default子句。

但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。

2.2.3 练习

这里给大家五分钟的时间,想一想这个程序的结果是什么?

int main()
{
    int n = 1;
    int m = 2;
    switch (n)
    {
    case 1:
        m++;
    case 2:
        n++;
    case 3:
        switch (n)
        {
        case 1:
            n++;
        case 2:
            m++;
            n++;
            break;
        }
    case 4:
        m++;
        break;
    default:
        break;
    }
    printf("m = %d, n = %d\n", m, n);
    return 0;
}

补充:switch语句是可以嵌套使用的,break语句只会跳出一层循环

结果是:

在这里给大家讲解一下:

int main()
{
    int n = 1;
    int m = 2;
    switch (n)//n = 1,走case 1的情况
    {
    case 1:
        m++;//m = 3,没有break,按顺序往下执行
    case 2:
        n++;//n = 2
    case 3:
        switch (n)//switch嵌套,n = 2
        {
        case 1:
            n++;
        case 2://直接来这
            m++;//m = 4
            n++;//n = 3
            break;//跳出内层switch循环
        }
    case 4://来到这
        m++;//m = 5
        break;跳出外层switch循环
    default:
        break;
    }
    printf("m = %d, n = %d\n", m, n);//直接来到这,打印 m = 5, n = 3
    return 0;
}

3、循环语句

  • while

  • for

  • do while

3.1 while循环

我们已经掌握了,if语句:

if(条件)
{
    语句;   
}

当条件满足的情况下,if语句后的语句执行,否则不执行。但是这个语句只会执行一次。

由于我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次。那我们怎么做呢?

C语言中给我们引入了: while 语句,可以实现循环。

//while的语法结构
while(条件)
{
    循环语句;
}

这个就是while循环的执行流程

比如我们实现:

在屏幕上打印1-10的数

int main()
{
    int i = 1;
    while (i <= 10)//条件是i的范围,符合则进来
    {
        printf("%d ", i);//打印i
        i = i + 1;//i不断加1,再次回到判断条件符合则进行下一次循环
    }
    return 0;
}

上面的代码已经帮我们了解了 while 语句的基本语法,那我们再了解一下while语句中的break和continue

3.1.1 while语句中的break

接下来请大家思考以下代码的结果

int main()
{
    int i = 1;
    while (i <= 10)
    {
        if (i == 5)
            break;
        printf("%d ", i);
        i = i + 1;
    }
    return 0;
}

运行的结果是下列哪一个呢?

1 2 3 4

1 2 3 4 5

1 2 3 4 5 6 7 8 9 10

1 2 3 4 6 7 8 9 10

是第一个!

我们发现当i打印4以后,i = i + 1变成5,再次进入循环此时if条件为真,执行break,我们调试看一下,执行break前后代码该到哪里接着执行

现在是第20行,按F10执行下一步,我们看

黄色的箭头直接来到了24行,直接跳出了循环,再按一下F10程序就运行结束,所以打印1 2 3 4

总结:

break在while循环中的作用:

其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。

所以:while中的break是用于永久终止循环的。

3.1.2 while语句中的continue

接下来请大家思考以下代码的结果

int main()
{
    int i = 1;
    while (i <= 10)
    {
        if (i == 5)
            continue;
        printf("%d ", i);
        i = i + 1;
    }
    return 0;
}

运行的结果又会是下列哪一个呢?

1 2 3 4

1 2 3 4 5

1 2 3 4 5 6 7 8 9 10

1 2 3 4 6 7 8 9 10

是第一个!当i==5条件为真,执行完continue后,我们发现后面再也没有打印出来任何的值

我们再来看下面这个代码

我们把i=i+1的这一行代码挪到if之前,我们发现结果全然不同,但是结果中并没有打印5这个数,那我们调试一下来观察continue的作用

再次按F10

直接来到了循环的判断条件-45行,跳过了第50行打印i的这句代码,直接来到下一次循环

总结

continue在while循环中的作用就是:

continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分,进行下一次循环的入口判断。

3.1.3 实景演绎

在基本介绍完这些while语句中的语法问题时,用一个现实生活中的场景带大家再感受感受---密码匹配

先来介绍一下要用到的两个库函数---getchar和putchar

getchar - 从键盘获取字符
EOF - end of file - 文件结束标志
#define EOF    (-1)
在函数读取失败的时候返回了EOF
scanf 函数读取成功,返回的是读取到的数据的个数,读取失败返回EOF
getchar 读取成功返回字符的ASCII码值,读取失败返回EOF
ctrl+Z - 回让scanf 或者 getchar 返回EOF
putchar用来打印字符

从图片中我们得知,putchar和用printf打印字符是一个作用

int main()
{
    int ch = 0;
    while ((ch = getchar()) != EOF)
    {
        putchar(ch);
    }
    return 0;
}

实现的效果如图

我们发现都没有让我们输出Y直接就匹配失败了这是为什么呢?

那我们现在修改一下这个代码,把\n读走就可以了

这样就成功实现我们的功能了,那我们换个复杂的密码试一试

相同的问题再一次出现了,又一次确认失败,这是因为

scanf会把空格前面的拿走,而更改后加入的getchar();把空格读取走了,剩下的是abc\n所以又一次失败了,而我们需要让getchar读取走缓冲区里的所有内容一直到\n,把\n也读取走

这样我们想实现的功能就彻底实现啦!!!

我们再来看一个代码

int main()
{
    char ch = '\0';
    while ((ch = getchar()) != EOF)
    {
     if (ch < '0'|| ch > '9')//字符在内存中是以ASCII码存储的,符合判断条件的都是除了数字的其他字符
        continue;
     putchar(ch);
    }
    return 0;
}
//这个代码的作用是:只打印数字字符,跳过其他字符的

输入1以后并没有换行,输入q光标就来到下一行了,这里的1后面有个回车但是不打印,所以1的后面没有换行

3.2 for循环

我们用while循环在屏幕上来打印1-10的数字

3.2.1 语法
for(表达式1; 表达式2; 表达式3)
 循环语句;

表达式1

表达式1为初始化部分,用于初始化循环变量的。

表达2

表达式2为条件判断部分,用于判断循环时候终止。

表达式3

表达式3为调整部分,用于循环条件的调整。

那我们用for循环再来实现一下在屏幕上打印1-10

int main()
{
    int i = 0;
    for (i = 1; i <= 10; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

我们把这两种循环比较能发现,for循环将影响循环的三个部分整合到了一起,图中相同颜色是对应的

给大家看一下for循环的程序流程图,是不是对这个循环有了自己的认识呢?

我们在初始化以后,就是在反复的进行判断-循环体语句执行-循环变量调整这三部分

可以发现在while循环中依然存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较

远,这样查找修改就不够集中和方便。所以,for循环的风格更胜一筹;for循环使用的频率也最高。

3.2.2 for循环的break

我们发现在for循环中也以出现break,他的意义和在while循环中是一样的。

int main()
{
    int i = 0;
    for (i = 1; i <= 10; i++)
    {
        if (i == 5)
            break;
        printf("%d ", i);
    }
    return 0;
}

这个代码的运行结果是什么呢?

这里的break也是直接跳出循环,大家可以自己动手调试一下

3.2.3 for循环的continue

我们发现在for循环中也以出现continue,他的意义和在while循环中是有区别的

int main()
{
    int i = 0;
    for (i = 1; i <= 10; i++)
    {
        if (i == 5)
            continue;
        printf("%d ", i);
    }
    return 0;
}

这个代码的运行结果是什么呢?

这里的continue就有所不同了,我们说while循环中的continue是跳过后续的代码,来到判断条件的地方。而for循环这里则是来到了循环变量调整的地方,正是因为循环变量调整这一部分位置的不同才导致了不一样的结果

3.2.4 for语句的循环控制变量

建议1:不可在for循环体内修改循环变量,防止 for 循环失去控制。

用代码为大家讲解

int main()
{
    int i = 0;
    for (i = 1; i <= 10; i++)
    {
        if (i = 5)//我在这里对i进行修改,对i赋值
            continue;
        printf("%d ", i);
    }
    return 0;
}

我们发现结果死循环了,这是因为在循环体里对i赋值以后,下一次循环判断条件始终都为真,为真就执行continue,循环往复,最终死循环了

建议2:for语句的循环控制变量的取值采用“前闭后开区间”写法。

int main()
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", i);
    }
    return 0;
}
int main()
{
    int i = 0;
    for (i = 0; i <= 9; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

上述两种代码都对,但我们更推荐第一种,没有特殊的意义,第一种可以直观的告诉我们循环了多少次

补充:

int main()
{
    for (int i = 0; i <= 9; i++)//这种循环变量初始化的写法在C99标准中引入
    //支持C99标准的编译器可以这么使用,这种写法是在C++中可以使用的
    //如果你的编译器不支持C99标准,那么这种写法就是不行的
    {
        printf("%d ", i);
    }
    return 0;
}
3.2.5 一些for循环的变种

变种1:

int main()
{
    //初始化和调整部分的省略就是啥都不做
    //判断部分省略了,意味着判断是恒为真的
    //建议不要随便省略
    for (;;)
    {
        printf("hehe\n");
    }
    return 0;
}

运行以后我们发现程序死循环了

变种2:

int main()
{
    int i = 0;
    int j = 0;
    //这里打印多少个hehe?
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            printf("hehe\n");
        }
    }
    return 0;
}

一共是9个hehe,外层for循环执行3次,每一次外层for循环都会使内层for循环执行3次,所以一共是3*3=9次

变种3:

int main()
{
    int i = 0;
    int j = 0;
    //如果省略掉初始化部分,这里打印多少个hehe?
    for (; i < 3; i++)
    {
        for (; j < 3; j++)
        {
            printf("hehe\n");
        }
    }
    return 0;
}

为什么一共才打印了3个hehe呢?因为i=0时第一次进入循环,内层for循环使循环变量j从0最终加到3,此时内层循环结束,执行第二次外层循环,此时i是2,再次进入内层for循环,但这次因为没有初始化循环变量,j还是3内层循环不会执行,所以外层不论多少次循环,内存循环也不再执行,所以只打印出了3个hehe

变种4:

int main()
{
    int x, y;
    for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
    {
        printf("hehe\n");
    }
    return 0;
}

可以使用多余一个变量控制循环,三个影响循环的部分可以是表达式,表达式拥有几个变量我并不关心,我只关心哪个是初始化,哪个是判断,哪个是调整

3.2.6 一道经典笔试题
//请问循环要循环多少次?
#include <stdio.h>
int main()
{
    int i = 0;
    int k = 0;
    for (i = 0, k = 0; k = 0; i++, k++)
        k++;
    return 0;
}

执行0次,因为判断条件中k=0是把0赋值给k,这个表达式的结果是0,0为假所以判断失效不成立,所以执行0次

3.3 do while循环
3.3.1 do while循环语句语法
do
    循环语句;
while(表达式);
3.3.2 执行流程
int main()
{
    int i = 1;
    do
    {
        printf("%d ", i);//上来就执行依次循环
        i = i + 1;
    } while (i <= 10);//判断条件
    return 0;
}
3.3.3 do while循环语句的特点

循环至少执行一次,使用的场景有限,所以不是经常使用

我们再来用do while循环实现打印1-10的数字

int main()
{
    int i = 1;
    do
    {
        printf("%d ", i);//上来就执行依次循环
        i = i + 1;
    } while (i <= 10);//判断条件
    return 0;
}
3.3.4 do while循环中的break
int main()
{
    int i = 1;
    do
    {
        if (5 == i)
            break;
        printf("%d ", i);
        i = i + 1;
    } while (i <= 10);
​
    return 0;
}

我们来调试一下就知道这里的break是什么作用了

现在符合if的条件来到了第22行,再按一下F10

我们发现直接跳出循环来到了27行,这里的break也是跳出循环的意思

3.3.4 do while循环中的continue
int main()
{
    int i = 1;
    do
    {
        if (5 == i)
            continue;
        printf("%d ", i);
        i = i + 1;
    } while (i <= 10);
​
    return 0;
}

我们来调试一下就知道这里的continue是什么作用了

现在符合if的条件来到了第22行,再按一下F10

此时来到了第39行,这里的continue则是跳过循环中的后续代码,来到了while判断条件的地方

3.4 练习

在这里给大家出了几道小题,大家动手做做,不明白的可以评论私信我

  1. 计算 n的阶乘。

    //不考虑溢出
    int main()
    {
        int n = 0;
        scanf("%d", &n);
        int i = 0;
        int ret = 1;
        for (i = 1; i <= n; i++)
        {
            ret = ret * i;
        }
        printf("%d\n", ret);
    ​
        return 0;
    }
  2. 计算 1!+2!+3!+……+10!

    //方法一
    int main()
    {
        int i = 1;
        int ret = 1;
        int sum = 0;
        for (i = 1; i <= 10; i++)
        {
            ret = ret * i;
            sum = sum + ret;
        }
        printf("%d", sum);
        return 0;
    }
    //方法二
    int main()
    {
        int n = 0;
        int i = 0;
        int ret = 1;
        int sum = 0;
        
        for (n = 1; n <= 3; n++)
        {
            ret = 1;//防止累计,重新初始化
            for (i = 1; i <= n; i++)
            {
                ret = ret * i;
            }
            sum += ret;
        }
    ​
        printf("%d\n", sum);
    ​
        return 0;
    }
  3. 在一个有序数组中查找具体的某个数字n。

    //第一种方法是从头到尾遍历,挨个进行比较
    int main()
    {
        int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//升序
        int k = 17;
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            if (arr[i] == k)
            {
                printf("找到了,下标是:%d\n", i);
                break;
            }
        }
        if (i == 10)
        {
            printf("找不到了\n");
        }
        return 0;
    }
    //第二个方法是折半查找算法,对初学者有一些难度,掌握好思路是重点
    //首先讲一种获得数组元素个数的方法---涉及到sizeof
    int main()
    {
        int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
        printf("%d", sizeof(arr));//计算的数组的总大小,单位是字节
        printf("%d", sizeof(arr[0]));//一个int类型的元素大小是4个字节
        printf("%d", sizeof(arr) / sizeof(arr[0]));//数组中的元素个数
        return 0;
    }
    int main()
    {
        int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//所查找的有序数组
        int k = 7;//7是我们要找的元素
        int sz = sizeof(arr) / sizeof(arr[0]);//获取元素个数
        //1、获取左右范围
        int left = 0;
        int right = sz - 1;
        int flag = 0;//flag的作用是标志是否找到了
        //2、循环查找
        while (left <= right)
        {
            int mid = (left + right) / 2;//根据下标找到中间值
            if (arr[mid] == k)
            {
                printf("找到了,下标是:%d\n", mid);
                flag = 1;//找到则修改标记
                break;//直接跳出循环
            }
            else if (arr[mid] < k)
            {
                left = mid + 1;//更新查找范围
            }
            else
            {
                right = mid - 1;//更新查找范围
            }
        }
        //3、查找失败
        if (flag == 0)
        {
            printf("没找到\n");
        }
        return 0;
    }

4.编写代码,演示多个字符从两端移动,向中间汇聚。

//实现效果
//Learn programming with me
​
//#########################
//L#######################e
//Le#####################me
//Lea################### me
//Lear#################h me
//Learn###############th me
//...
//Learn programming with me
#include <string.h>
#include <windows.h>
//Sleep需要一个windows.h的头文件
#include <stdlib.h>
//system需要一个stdlib.h的头文件
​
int main()
{
    char arr1[] = "Learn programming with me";
    char arr2[] = "#########################";
    int left = 0;
    int right = strlen(arr1)-1;//strlen是计算字符串长度的库函数,计算\至0之前
    //这里不能用sizeof,因为字符串末尾有\0,要想使用需要把\0也减去
    //int right = sizeof(arr1) - 2;
​
    while (left<=right)
    {
        arr2[left] = arr1[left];
        arr2[right] = arr1[right];
        printf("%s\n", arr2);
        Sleep(1000);//单位是毫秒,让执行的上下语句间隔1000毫秒=1秒
        system("cls");//system函数可以执行系统命令,cls是清理屏幕,使结果一行一行展现
        left++;
        right--;
    }
    printf("%s\n", arr2);
    return 0;
}

5.编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则

提示登录成,如果三次均输入错误,则退出程序。

//假设密码是:"123456"
​
int main()
{
    int i = 0;
    char password[20] = {0};
    for (i = 0; i < 3; i++)//题目要求只能登陆三次,所以循环三次
    {
        printf("请输入密码:>");
        scanf("%s", password);
        //在这里强调一下,判断密码是否正确
        //if(password == "123456")这是错误的
        //两个字符串比较相等是不能使用==的,应该使用strcmp库函数
        //strcmp返回0表示2个字符串相等
        //strcmp返回>0的数字,表示第一个字符串大于第二个字符串
        //strcmp返回<0的数字,表示第一个字符串小于第二个字符串
        //两个字符串的大小是通过比较相同位置的字符的ASCII码值得出的
        //abcd\0
        //abq
        if (strcmp(password, "123456") == 0)
        {
            printf("登录成功\n");
            break;
        }
        else
        {
            printf("密码错误\n");
        }
    }
    //如果登录成功则i一定小于三,如果循环结束来到这i=3.则是登陆失败,退出程序
    if (i == 3)
        printf("退出程序\n");
​
    return 0;
}
3.5 猜数字游戏

在这里把我们前面所学的所有知识点整合在一起,在这里从无到有为大家做一个小游戏

首先我们要确定这个游戏我们要实现的功能

猜数字游戏

1、电脑要随机生成一个数

2、猜数字

a>猜大了,提示猜大了,继续猜

b>猜小了,提示猜小了,继续猜

c>猜对了,恭喜你,猜对了,继续游戏

3、玩完一把可以接着玩,不用退出程序

那知道了这些功能以后,我们就可以开始写代码了

#include<stdio.h>
​
void menu()//菜单函数,在屏幕上打印菜单,让游戏界面显得美观
{
    printf("***************************\n");
    printf("***** 1.play   0.exit *****\n");
    printf("***************************\n");
}
​
int main()
{
    int input = 0;
    menu();//调用菜单函数
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)//根据输入的值匹配对应的情况
    {
    case 1:
        printf("开始游戏\n");
        break;
    case 0:
        printf("退出游戏\n");
        break;
    default:
        printf("选择错误\n");
        break;
    }
    return 0;
}

我们在未来的开发种应该做到边写边调试,而不是从第一行写到最后,结果一编译200个错误

我们调试

我们发现运行没有问题,那我们接着会发现要求是玩完一把可以接着玩,所以我们需要用到循环,而根据实际情况,需要先打印菜单,所以用do while循环比较合适,同时我们要把游戏的功能封装在game函数中,在case1中实现

void game()
{
    //1. 生成随机数
    int ret = rand();//可以生成随机数,随机数的范围是:0~32767
    printf("%d\n", ret);
}

我们双击选中点击速览定义

我们发现这是定义好的,最大值就是0x7fff

这个0x7fff就是十进制的32767,大家下去可以用计算器算一下

然后我们运行看一看

我们退出程序再次运行发现跟上面的结果一致

rand函数确实可以给我们生成随机数,在一次运行内部好像很随机,但本次运行和下一次运行的结果却有问题,那么问题出现在哪里呢?其实是我们这个函数用错了,那这个函数我们怎么用呢?

我们通过在cplusplus.com中就可以找到这两个函数的介绍,我们发现在rand函数调用前需要调用srand,用来设置随机数的生成起点

而srand函数是初始化随机数的生成器,返回值是空,参数类型是unsigned int

那是不是意味着直接使用它就可以了呢?

我们发现每次生成的随机数都是41,是因为srand(1)中的1是固定的,改称2或者100运行会是不同的值

因为里面的数是不变的所以我们生成的随机数也是不变的,只有里面是随机数,生成的随机数才是变的

所以我们要给srand传一个随机数,然后又用rand生成一个随机的值,这就形成了一个先有鸡还是先有蛋的问题,我们本来要生成一个随机数,结果srand又需要一个随机数,有点尴尬

其实我们不是要传随机数,是给srand传一个变化的值

void game()
{
    RAND_MAX;
    //1. 生成随机数
    srand();//要给srand传递一个变化的值,计算机上的时间是时刻发生变化的
    //这时候就可以使用时间戳
    int ret = rand();//可以生成随机数,随机数的范围是:0~32767
    printf("%d\n", ret);
}

任何一个时间都可以转换一个对应的值(单位为秒的数字),任何一个值都可以转换成一个对应的时间,时间在变,时间戳就变,这个值就是时间戳

那我们是不是就可以传一个时间戳给srand函数呢?那么这个时间戳怎么产生呢?

C语言中的time函数可以返回一个时间戳,那我们怎么用呢?

参数是指针类型,不用就可以传一个空指针,返回值我们得用,返回的就是时间戳

我们发现time_t本质上就是long long整型

void game()
{
    RAND_MAX;
    //1. 生成随机数
    srand((unsigned int)time(NULL));//要给srand传递一个变化的值,计算机上的时间是时刻发生变化
    //time函数可以返回一个时间戳,返回值类型是time_t
    //srand函数返回值类型是unsigned int
    //在这里我们将他强制转换
    int ret = rand();//可以生成随机数,随机数的范围是:0~32767
    printf("%d\n", ret);
}

当我们运行会发现确实生成了随机数,但是每个之间离得很近,我们快速运行几次就会发现出现了相同的随机数,当前这种生成随机数的方法也是有问题的,我们确实已经找到了生成随机数的方法,但是这个srand不能频繁使用,不能说每一次生成随机数都设置一个随机数生成起点,这个srand函数在整个工程里面只调用一次就可以了,所以我们将它挪到主函数中,这样它只执行一次,执行完以后,后面就可以放心大胆的使用rand函数了,这是规则,就应该这么用,这样生成的随机数才非常的随机

#include <stdio.h>
#include <stdlib.h>
#include <time.h>//time函数需要头文件
​
void menu()
{
    printf("***************************\n");
    printf("***** 1.play   0.exit *****\n");
    printf("***************************\n");
}
​
void game()
{
    RAND_MAX;
    //1. 生成随机数
    int ret = rand();//可以生成随机数,随机数的范围是:0~32767
    printf("%d\n", ret);
}
​
int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));//要给srand传递一个变化的值,计算机上的时间是时刻发生变化
    //#define NULL ((void *)0)  NULL的定义
    //time函数可以返回一个时间戳,返回值类型是time_t
    //srand函数返回值类型是unsigned int
    //在这里我们将他强制转换
    do
    {
        menu();//调用菜单函数,在屏幕上打印菜单,让游戏界面显得美观
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

运行发现,我们按多快,都是很随机的数

为了让游戏更有意思,我们将随机数的范围设置在1-100之间

void game()
{
    RAND_MAX;
    //1. 生成随机数
    //int ret = rand();//可以生成随机数,随机数的范围是:0~32767
    int ret = rand()%100+1;
    //任何一个数字模100的余数的取值范围只可能是0-99
    //给这样的余数+1就是1-100
}

同样猜数字也需要根据一开始我们确定的功能来实现,需要用到循环

void game()
{
    RAND_MAX;
    //1. 生成随机数
    int ret = rand() % 100 + 1;//可以生成随机数,随机数的范围是:0~32767
    //2、猜数字
    int guess = 0;
​
    while (1)//判断条件-死循环,一直猜
    {
        printf("请猜数字:>");
        scanf("%d", &guess);
        if (guess > ret)
        {
            printf("猜大了\n");
        }
        else if (guess < ret)
        {
            printf("猜小了\n");
        }
        else
        {
            printf("恭喜你,猜对了\n");
            break;//猜对了循环停下里
        }
    }
}

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
​
void menu()
{
    printf("***************************\n");
    printf("***** 1.play   0.exit *****\n");
    printf("***************************\n");
}
​
void game()
{
    RAND_MAX;
    //1. 生成随机数
    int ret = rand() % 100 + 1;//可以生成随机数,随机数的范围是:0~32767
    //2、猜数字
    int guess = 0;
​
    while (1)
    {
        printf("请猜数字:>");
        scanf("%d", &guess);
        if (guess > ret)
        {
            printf("猜大了\n");
        }
        else if (guess < ret)
        {
            printf("猜小了\n");
        }
        else
        {
            printf("恭喜你,猜对了\n");
            break;
        }
    }
}
​
​
int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));//要给srand传递一个变化的值,计算机上的时间是时刻发生变化
    //#define NULL ((void *)0)  NULL的定义
    //time函数可以返回一个时间戳,返回值类型是time_t
    //srand函数返回值类型是unsigned int
    //在这里我们将他强制转换
    do
    {
        menu();//调用菜单函数,在屏幕上打印菜单,让游戏界面显得美观
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

运行来玩一玩吧

这样我们就成功实现啦!!!快给自己鼓个掌吧!

3.6 goto语句

C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。

从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。

但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。

例如:一次跳出两层或多层循环。

多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。

goto语言真正适合的场景如下:

for(...)
    for(...)
   {
        for(...)
       {
            if(disaster)
                goto error;
       }
   }
    …
error://标签,goto语句可以跳到程序前面,也可以跳到程序后面
 if(disaster)
         // 处理错误情况

举个例子:

int main()
{
again://标签,顶头写
    printf("good good study\n");
    printf("day day up\n");
    printf("一起学习\n");
    goto again;
​
    return 0;
}

调试我们发现,这个运行又一次来到了75行,这个程序就死循环了

同时

下面是使用goto语句的一个例子,一个关机程序

//关机程序
//1. 程序运行起来后,1分钟内电脑关机
//2. 如果输入:我是猪,就取消关机
​
#include <stdlib.h>
#include <string.h>
​
int main()
{
    char input[20] = { 0 };//用一个字符数组存输入的内容
    //程序倒计时关机
    system("shutdown -s -t 60");
    //system可以执行系统命令,而这里的shutdown -s -t 60就是还有60秒关机
again:
    printf("请注意,你的电脑在1分钟内关机,如果输入:我是猪,就取消关机\n");
    scanf("%s", input);//输入
    if (strcmp(input, "我是猪") == 0)//字符串比较用strcmp函数
    {
        system("shutdown -a");//取消关机命令
    }
    else
    {
        goto again;//直接跳转,循环输入
    }
    return 0;
}

我们按住键盘的win+r键,

点击确定

在这里我们可以各种各样的命令来控制电脑

点击回车,我们发现系统提示关机,这时我们赶紧取消,用shutdown -a即可

而system就是在C语言程序中执行系统命令

好啦!!!到这里第二个大部分就彻底结束啦!!!再接再厉学到这你已经很厉害了!!!给自己竖一个大拇指!!!你是最棒的!!!今天小周就带大家学到这里。小周写一篇高质量详细的博客也也花了好多时间,看到这里请动动你的小手来为小周点赞推广评论一下吧!!!你们的点赞推广评论是我继续输出高质量博客的动力!!!谢谢大家!!!

欲知后事如何,且听下回分解

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值