c语言:分支语句和循环语句

分支语句和循环语句

分支语句

  • if
  • switch

循环语句

  • while
  • for
  • do while

goto 语句

C语言是结构化的程序设计语言,分为顺序结构,选择结构,循环结构

1.什么是语句?

C语句可以分为以下五类:

  1. 表达式语句  3 + 5;
  2. 函数调用语句  printf("hehe");
  3. 控制语句
  4. 复合语句
  5. 空语句  ;

控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句.

可分为以下三类:

  1. 条件判断语句也叫分支语句:if语句,switch语句`;
  2. 循环执行语句:do while语句,while语句,for语句`;
  3. 转向语句:break语句,goto语句,continue语句,return语句.

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

如果好好学习,将来你会有一个更好的选择.
如果不学习,回家卖红薯.
这就是选择!

2.1 if语句

if语句的语法结构:

//单分支
if(表达式)
    语句;

//双分支
if(表达式)
    语句;
else
    语句;

//多分支
if(表达式1)
    语句;
else if (表达式2)
    语句;
else
    语句;
//代码1
#include <stdio.h>

int main(void)
{
    int age = 0;

    scanf("%d", &age);
    //单分支
    if(age < 18)
    {
        printf("未成年\n");
    }

    return 0;
}

//代码2
#include <stdio.h>

int main(void)
{
    int age = 0;

    scanf("%d", &age);
    //双分支
    if(age < 18)
    {
        printf("未成年\n");
    }
    else
    {
        printf("成年\n");
    }

    return 0;
}

//代码3
int main(void)
{
    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");
    }

    return 0;
}

在选择结构中,只执行 if表达式为真时后面的语句块若表达式全为假,则都不执行,有 else时执行 else后面的语句块

C语言中,0表示假,非0表示真

2.1.1 悬空else

当写了如下代码

#include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 2;

    if (a == 1)
        if (b = 2)
            printf("hehe\n");
    else
        printf("haha\n");

    return 0;
}

上述代码并没有任何结果
根据原作者的代码缩进可以得出,else本应表示 a != 1的情况,但C语言会忽视代码缩进,else和自己最近的 if进行匹配,其实判断完 a == 1为假后,直接跳出了判断,代码终止.

这就需要我们需要良好的代码风格,应如下

#include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 2;

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

上述代码打印出了 haha.
同时,判断变量是否等于一个字面量时,更推荐类似于上面 1 == a的写法,这样如果中间的 ==误写成 =时,编译器会直接编译不通过,减少许多麻烦.养成良好的代码风格是十分重要的.
这里推荐一本教程: 林锐 博士 出版的<高质量的C/C++编程风格>

2.1.2 if书写形式的对比

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

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

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

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

上述的代码1和代码2,代码3和代码4,所表示的逻辑是一样的,但更推荐代码2和代码4的写法
代码2和代码4逻辑清晰,不容易出错,写出一个不仅自己能看得懂,更能让其他人也能快速明白程序的逻辑的代码,这是一个程序员必备的能力.

2.1.2 练习

  1. 判断一个数是否为奇数
  2. 输出 1-100 之间的奇数
//1.判断一个数是否为奇数
#include <stdio.h>

int main(void)
{
    int num = 0;

    scanf("%d", &num);

    if (num % 2 == 1)
    {
        printf("%d是奇数\n");
    }
    else
    {
        printf("%d是偶数\n");
    }

    return 0;
}

//2.输出 1-100 之间的偶数
# include <stdio.h>

int main(void)
{
    int i = 1;

    while (i <= 100)
    {
        //判断如果i是偶数的话,就输出i
        if (i % 2 == 0)
        {
            printf("%d ", i);
        }
        ++i;
    }

    return 0;
}

2.2 switch语句

switch语句也是一种分支语句.常常用于多分支的情况.
比如:

输入1,输出星期一
输入2,输出星期二
输入3,输出星期三
输入4,输出星期四
输入5,输出星期五
输入6,输出星期六
输入7,输出星期日

相比 if...eles if...else if...else的形式,switch语句更为简洁

switch(整型表达式)
{
    case 整型常量表达式:
        语句;
    case 整型常量表达式:
        语句;
        ...
}

需要注意的是,C语言的 switch语句只支持整型表达式.整型常量表达式简便点说就是整型字面量.
学习完 switch语句,对于上面的多分支情况,可能会打出如下代码

#include <stdio.h>

int main(void)
{
    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;
}

发现并没有预期的效果,这里就要引入 break关键字.

2.2.1 在switch语句中的break

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

#include <stdio.h>

int main(void)
{
    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;
    }

    return 0;
}

这样才能达到我们一开始的需求.

但有时候需求变了:

  1. 输入1-5,输出weekday
  2. 输入6-7,输出weekend
    代码应该这样实现:
#include <stdio.h>

int main(void)
{
    int day = 0;
  
    scanf("%d", &day);
    switch (day)
    {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
            printf("weekday\n");
            break;
        case 6:
        case 7:
            printf("weekend\n");
            break;
    }

    return 0;
}

所以说,并不是每一分支都必须用到 break,关键看需求,break的作用就是跳出循环,在这的效果就是实现了 switch语句的分支结构.

2.2.2 default语句

如果表达的值与所有的case标签的值都不匹配怎么办?
其实也没什么,结构就是所有的语句都被跳过而已.
程序并不会终止,也不会报错,因为这种情况中在C中并不认为是个错误.
但是如果不想忽略不匹配所有标签的表达式的值应该怎么办呢?
可以在语句列表中增加一条 default语句.
default可以出现在任何一个 case语句出现的位置上.
switch表达式的值并不匹配所有的 case标签的值时,这个 default子句后面的语句就会执行.
所以,每个 switch语句中只能出现一条 default子句.
但是他可以出现在语句列表的任何位置,而且语句流会像执行一个 case标签一样执行 default子句.

2.2.3练习

#include <stdio.h>

int main(void)
{
    int n = 1;
    int m = 2;
    switch (n)
    {
    case 1:
        m++;                //执行 m = 3
    case 2:
        n++;                //执行 n = 2
    case 3:
        switch (n)
        {
            case 1:
                n++;        //不执行
            case 2:
                m++;
                n++;        //执行 m = 4, n = 3
                break;
        }
    case 4:
        m++;                //执行 m = 5
        break;
    default:
        break;
    }

    printf("m = %d, n = %d\n", m, n);

    return 0;
}

最终结果是 m = 5, n = 3

3. 循环语句

  • while
  • for
  • do while

3.1 while循环

我们已经掌握了,if语句

if(condition)
{
    suit;
}

当这个条件满足的情况下,if语句后的语句执行,否则不执行.
但是这个语句只会执行一次.
由于我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次.
那我们怎么做呢?
C语言给我们引入了:while语句,可以实现循环.

while(condition)
{
    suit;
}

while语句执行的流程:
请添加图片描述

比如实现:

在屏幕上打印1-10的数字

#include <stdio.h>

int main(void)
{
    int i = 1;
    while (i <= 10)
    {
        printf("%d ", i);
        ++i;
    }

    return 0;
}

3.1.1 while语句中的break和continue

break介绍

#include <stdio.h>

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

    return 0;
}

上述代码输出的结果是1 2 3 4

总结:
breakwhile循环中的作用:

在循环中,只要遇到break,就终止所有后续代码的执行,并且跳出循环.
while中,break的作用就是永久终止循环

continue介绍

#include <stdio.h>

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

    return 0;
}

上述代码输出的结果是1 2 3 4,并且程序没有终止.
++i移到判断前呢

#include <stdio.h>

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

    return 0;
}

上述代码的结果是2 3 4 6 7 8 9 10 11

总结:
continue在循环中的作用:
终止本次循环中continue后的代码,直接去while的判断部分.

最后再看几个代码

  • 代码1
#include <stdio.h>

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

    return 0;
}

这个代码内容是读取你输入的字符并且将它输出到屏幕中,直到输入EOF

  1. EOF:end of file 在C语言对应的值是-1,因为字符类型的值是从0开始的
    windows输入ctrl + z来表示EOF
  2. getchar():读取成功,返回读取的ASCII码值
         读取失败,返回EOF

对这个代码进行适当的修改,是可以用来清理缓存区的
这里引入了缓存区的概念,先从scanf()getchar()读取输入的过程来说
以输入密码并确定这一操作来举例

#include <stdio.h>

int main(void)
{
    char password[20] = {0,};
    char input = 0;

    printf("请输入你的密码:");
    scanf("%s", password);

    printf("请确认密码(Y/N)");
    scanf("%c", &input);
    if ('Y' == input)
    {
        printf("确认成功\n");
    }
    else
    {
        printf("确认失败\n");
    }

    return 0;
}

运行上述代码发现,输入密码后,没有给我确认的机会就直接确认失败了
现在研究一下这个代码的过程

  1. 键盘输入,输入的值存入缓存区.
    比如我输入123456后,输入回车,表示我输入完毕
    这时候缓存区里面存放了123456\n
  2. 第一次调用scanf(),scanf()从缓存区里读取\n之前的输入,如果此时缓存区空,则该函数进行等待
  3. 第二次调用了scanf(),这时缓存区还剩\n,scanf()直接读取了\n并将该值与input进行绑定
  4. 最终确认失败

那该如何修改这段代码,使之能满足我们的需求呢?
这里就要用到刚才说的,清理缓存区的方式,在输入input的值之前,将缓存区清理干净

进行了这样的修改后,就能达到需求了

#include <stdio.h>

int main(void)
{
    char password[20] = {0,};
    char input = 0;

    printf("请输入你的密码:");
    scanf("%s", password);

    printf("请确认密码(Y/N)");
    while(getchar() != '\n')
    {
        ;
    }
    input = getchar();
    if ('Y' == input)
    {
        printf("确认成功\n");
    }
    else
    {
        printf("确认失败\n");
    }

    return 0;
}

在给input输入值之前,我先将包含\n之前的所有字符都使用getchar()接收了,因为getchar()并没有左值,这些值可以说被清空了,这个时候缓存区已经空了.
随后我进行正常给input进行赋值,这个修改最终满足了需求.

  • 代码2
#include <stdio.h>

int main(void)
{
    char ch = '\0';
    while ((ch = getchar()) != EOF)
    {
        if (ch < '0' || ch > '9')
        {
            continue;
        }
        putchar(ch);
    }

    return 0;
}

这段代码从键盘读取输入,如果输入的不是0-9的数字,直接跳过本次循环.如果输入的时0-9的数字,打印数字字符.

3.2 for循环

这里有一个打印1-10的while循环代码

#include <stdio.h>

int main(void)
{
    int i = 1;                  //1.初始化

    while (i <= 10)             //2.判断
    {
        printf("%d ", i);
        ++i;                    //3.调整
    }

    return 0;
}

发现控制循环有三个步骤:1.初始化 2.判断 3.调整
但是,这只是一个简单的打印1-10的代码.如果需要执行的语句不只是printf()这一条,而是很多很复杂的语句,再找到对应的步骤就很困难,也很容易漏掉步骤,导致循环往出乎意料的结果发展.
相比,在确定循环首尾的情况下,for循环更为简洁明了,易于控制理解.

3.2.1 语法

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

表达式1
表达式1为初始化部分,只执行进入for循环条件判断之前的那一次

表达式2
表达式2为条件判断部分,用于判断循环是否终止

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

第一次进入for循环步骤:表达式1->表达式2->循环语句->表达式3
之后循环步骤;表达式2->循环语句->表达式3

使用for循环打印1-10代码如下

#include <stdio.h>

int main(void)
{
    int i = 0;

    //表达式1: i = 1   初始化
    //表达式2: i <= 10  条件判断 判断为真执行循环语句
    //表达式3: ++i     调整 
    for (i = 1; i <= 10; ++i)
    {
        printf("%d ", i);
    }

    return 0;
}

请添加图片描述

相比与while循环,for循环三个表达式更为集中,对循环的进出更为简洁明了.使用更为频繁.

3.2.2 break和continue在for循环中

for循环中也可以出现breakcontinue关键字来控制循环,它们在for循环中的功能和在while循环中的功能是一样的

  • break:
#include <stdio.h>

int main(void)
{
    int i = 0;

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

    return 0;
}

上面这段代码的结果是请添加图片描述

5 == i这个表达式判断为真后,执行相应的语句break,跳出循环,并没有继续进行打印和循环

  • continue
#include <stdio.h>

int main(void)
{
    int i = 0;

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

    return 0;
}

上面这段代码的结果式请添加图片描述
5 == i这个表达式判断为真后,执行相应的语句continue,跳过本次循环,没有继续进行本次循环中的打印,++i后条件判断为真,继续循环.

3.2.3 for语句的循环控制变量

建议:

  1. 在C语言中,建议不要在初始化时定义变量,这是C++的写法,C99标准后引入,若编译器不支持则会出现报错
  2. 不可在for循环体内修改循环变量,防止for循环失去控制
  3. 建议for语句的循环控制变量的取值采用"前闭后开区间"写法

3.2.4 一些for循环的变种

//代码1
#include <stdio.h>

int main(void)
{
    for(;;)
    {
        printf("hehe\n");
    }

    return 0;
}

上述代码,for循环的初始化部分,条件控制部分,调整部分全部省略.
初始化和调整省略,这个循环什么也没有做.
条件控制省略,判断条件恒为,循环可能跳不出去.
这段代码,在循环体内部也没有跳出循环的表达式,结果无限循环打印了hehe

//代码2
#include <stdio.h>

int main(void)
{
    int i = 0;
    int j = 0;

    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 4; ++j)
        {
            printf("hehe\n");
        }
    }
    return 0;
}

这段代码,for循环内嵌套了一个for循环
i == 0 : j == 0 -> j == 1 -> j == 2 -> j == 3
i == 1 : j == 0 -> j == 1 -> j == 2 -> j == 3
i == 2 : j == 0 -> j == 1 -> j == 2 -> j == 3
i == 3 : j == 0 -> j == 1 -> j == 2 -> j == 3
随后i == 4条件判断为假,跳出for循环,共打印16次hehe

//代码3
#include <stdio.h>

int main(void)
{
    int i = 0;
    int j = 0;

    for (; i < 4; ++i)
    {
        for (; j < 4; ++j)
        {
            printf("hehe\n");
        }
    }
    return 0;
}

但如果将初始化部分省略,则会出现这个结果请添加图片描述

i == 0 : j == 0 -> j == 1 -> j == 2 -> j == 3
i == 1 : j == 4
i == 2 : j == 4
i == 3 : j == 4
i == 0时正常执行,当第二次外循环开始后,因为缺少了初始化部分,j == 4并没有被更改,条件判断一直为假,内循环进不去,只打印了4次hehe.

//代码4
#include <stdio.h>

int main(void)
{
    int x, y;

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

    return 0;
}

可以使用多个变量来控制循环,三个部分放的是表达式,只要满足表达式的要求,都是可以被使用的.
上述代码打印了2次hehe

for循环的初始化部分,条件控制部分,调整部分是可以省略的,但初学不建议,容易导致问题.
省略需要根据实际业务需求进行省略,一切代码都是为了实现需求的.

//代码5
#include <stdio.h>

int main(void)
{
    int i = 0;
    int j = 0;

    for (i = 0, j = 0; j = 0; ++i, ++j)
    {
        ++j;
    }

    return 0;
}

上述代码的for循环体执行了0次,条件判断部分j = 0恒为假,不进入for`循环,循环体执行了0次.

3.3 do…while()循环

循环体至少执行1次

3.3.1 do语句的语法

do
    循环语句;
while(表达式);

3.3.2 执行流程

请添加图片描述

3.3.3 do语句的特点

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

使用do...while()打印1-10

#include <stdio.h>

int main(void)
{
    int i = 1;
    do
    {
        printf("%d ", i);
        ++i;
    } while (i <= 10);
    

    return 0;
}

3.3.4 do while循环中的break和continue

#include <stdio.h>

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

    return 0;
}

上述代码 打印出 1 2 3 4

#include <stdio.h>

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

    return 0;
}

上述代码 打印出 1 2 3 4 并且没有跳出循环

3.4 练习

3.4.1 计算n的阶乘

//不考虑溢出
#include <stdio.h>

int main(void)
{
    int n = 0;                  
    int ret = 1;                    //初识化结果变量

    scanf("%d", &n);                //输入n的值
    while (n > 0)                   //循环计算
    {
        ret *= n;                   
        --n;
    }
    printf("%d\n", ret);            //打印

    return 0;
}

3.4.2 计算1! + 2! + 3! +...+ 10!

第一种做法

#include <stdio.h>

int main(void)
{
    int n = 10;
    int ret = 1;                        //存储一项阶乘后的结果
    int sum = 0;                        //存储总和
    int i = 0;
    int j = 0;

    for (i = 1; i <= n; ++i)
    {
        ret = 1;                        //每进入一次内循环后需要初始化ret
        for (j = 1; j <= i; ++j)
        {
            ret *= j;                   //内循环计算每项阶乘的结果
        }
        sum += ret;                     //将每一项相加
    }
    printf("%d\n", sum);

    return 0;
}

得出结果4037913,这一代码可以进行优化.
发现每一次都需要重新初始化ret,并且重新计算对应项的阶乘,其实可以每次将计算的结果保存下来以供下一次使用,这样就少了一层循环,复杂度大大降低了.

改进后的代码:

#include <stdio.h>

int main(void)
{
    int n = 10;
    int ret = 1;
    int sum = 0;
    int i = 0;

    for (i = 1; i <= n; ++i)
    {
        ret *= i;                       //ret每次只需乘对应项就能得到该数的阶乘值
        sum += ret;
    }
    printf("%d\n", sum);

    return 0;
}

3.4.3 在一个有序数组中查找具体的某个数字n

拿到题目第一反应是遍历数组查找得到对应索引

#include <stdio.h>

int main(void)
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};    //定义有序数组
    int key = 7;                                    //待查找的值
    int len = 0;                                    //存放数组长度
    int i = 0;
    
    len = sizeof(arr) / sizeof(arr[0]);             //得到数组长度
    
    //遍历数组得到对应值的索引
    for (i = 0; i < len; ++i)   
    {                    
        if (arr[i] == key)
        {
            break;
        }
    }
    
    //如果i == len,则表示没查找到
    //如果i < len,则表示查找到了
    if(i == len)
    {
        printf("没有该值\n");
    }
    else if (i < len)
    {
        printf("找到%d,在数组的索引为%d\n", key, i);
    }

    return 0;
}

最终结果得到了7得索引6
这样确实可以得出结果,但如果数组的内容很大很大呢,不考虑内存的情况下,光是遍历数组就需要花费很多时间.
这里引入了一个查找有序数组的算法:二分查找

二分查找很符合我们的思维模式
如果猜一个范围为0-100的数,第一个猜的一般都是50,然后根据提示猜出的数是大是小再进行猜测25或者75,这样我们就能最多7次猜出这个数字了.

在这个题目里,一样可以运用二分查找思想来让我们相比遍历数组更快得到结果

  • 首先我们需要知道数组的首位置left和末位值right
  • leftright相加除2得到mid的值
  • 接下来将循环arr[mid]key相比较.
    arr[mid] == key,跳出循环,得到结果
    arr[mid] > key,下一次查找范围为前半部分即left ~ mid-1
    arr[mid] < key,下一次查找范围为后半部分即mid+1 ~ right
    这是这个题目使用二分查找的思路

还有一个关键点:何时跳出循环

  • 查找到值
  • 没有范围查找了, 即left > right

下面是实现代码:

#include <stdio.h>

int BinarySearch(int arr[], int size, int key);

int main(void)
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};    //定义有序数组
    int key = 7;                                    //待查找的值
    int size = sizeof(arr) / sizeof(arr[0]);        //存放数组长度
    int result = 0;                                 //存放结果

    result = BinarySearch(arr, size, key);
    if (result == -1)
    {
        printf("未找到\n");
    }
    else
    {
        printf("找到了,索引为%d", result);
    }
    
    return 0;
}

/*
函数内容:对有序数组进行二分查找
输入参数:1.arr[] 有序数组名 2.size 数组的大小 3.key 待查找的值
输出参数:查找结果
返回值:若查到,返回该值索引;若没有,返回-1
*/
int BinarySearch(int arr[], int size, int key)
{
    int left = 0;                                   //查找范围最左的索引
    int right = size - 1;                           //查找范围最右的索引
    int mid = 0;                                    //中间位置

    while (left <= right)
    {
        mid = (left + right) / 2;                   //计算mid的值
        if (arr[mid] == key)                        
        {
            return mid;                             //找到直接返回索引
        }
        else if (arr[mid] > key)
        {
            right = mid - 1;                        //比key大 right - 1
        }
        else if (arr[mid] < key)
        {
            left = mid + 1;                         //比key小 left + 1
        }
    }
    return -1;                                      //代码运行到这说明没有找到,返回-1
}

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

这道题用到了首尾指针向中间移动的思想

#include <stdio.h>
#include <string.h>
#include <windows.h>

int main(void)
{
    char arr1[] = "###############";
    char arr2[] = "Hello  World!!!";
    int left = 0;
    int right = strlen(arr1) - 1;
    
    while (left <= right)
    {
        arr1[left] = arr2[left];
        arr1[right] = arr2[right];
        printf("%s\n", arr1);
        Sleep(1000);                    //单位是毫秒
        system("cls");                  //清理屏幕
        ++left;
        --right;
    }

    return 0;
}

导入了两个新的库<string.h> 和 <windows.h>
使用strlen()计算字符串的长度,使用sizeof也可以,但因为字符串最后一位是'\0',得到的结果还需要再减一
使用Sleep()进行休眠操作,使用system("cls")进行清理屏幕操作

  1. 编写代码实现,模拟用户登陆情景,并且只能登陆三次.(只允许输入三次密码,如果密码正确则提示登陆成功,如果三次均输入错误,则退出程序)
#include <stdio.h>
#include <string.h>

int main(void)
{
    //假定密码是123456
    char password[20] = {0,};               //存放输入的密码
    int i = 0;
    int flag = 0;                           //密码正确为1, 三次均错误为0

    for (i = 0; i < 3; ++i)
    {
        printf("请输入密码:");
        scanf("%s", password);

        if (strcmp(password, "123456") == 0)
        {
            flag = 1;
            break;
        }
        else
        {
            printf("输入错误.\n");
        }
    }
    if (flag == 1)
    {
        printf("登陆成功\n");
    }
    else
    {
        printf("三次输入失败,登陆失败.\n");
    }
    
    return 0;
}

上述代码导入了<string.h>
使用strcmp()比较两个字符串内容是否相等:

  1. 相等返回0
  2. 第一个字符串大则返回1
  3. 第一个字符串小则返回-1

3.4.5 猜数字游戏

  1. 电脑会生成一个随机数
  2. 猜数字
    • 猜大了,提醒猜大了,继续猜
    • 猜小了,提醒猜小了,继续猜
    • 猜中了,提醒猜中了,游戏结束
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void Menu();
void Game();

int main(void)
{
    int choice = 0;

    do
    {
        Menu();
        printf("Please enter your choice>:");
        scanf("%d", &choice);
        srand((unsigned int)time(NULL));                //定义随机数起点,使用srand()和time()
        switch (choice)
        {
            case 1:
                Game();
                break;
            case 0:
                printf("Exit game.\n");
                break;
            default:
                printf("Your input is wrong, please re-enter your choice:\n");
                break;
        }
    } while (choice != 0);
    

    return 0;
}

void Menu()
{
    //游戏开始菜单
    //1.开始游戏  0.退出游戏
    printf("##########################\n");
    printf("####1.start     0.exit####\n");
    printf("##########################\n");
}

void Game()
{
    int random_num = rand() % 100 + 1;                  //随机一个1-100的数字
    int guess = 0;

    while (1)
    {
        printf("Please enter your guess.\n");
        scanf("%d", &guess);

        if (guess == random_num)
        {
            printf("You get it! Congraulations!\n");
            break;
        }
        else if (guess < random_num)
        {
            printf("Your guess is small, guess again.\n");
        }
        else
        {
            printf("Your guess is big, guess again.\n");
        }
    }
}

上述代码有几个关键点:

  1. 先构造框架,然后再实现具体功能,边打代码边调试
  2. rand()可以返回一个伪随机数,不需要输入参数,一次运行内得到的数不一样,但每次运行得到的数字都是一样的.
  3. 针对rand()的问题,可以使用srand()来重新定义随机数起点,只要srand()的参数在每次运行程序时不一样就可以了
  4. 时间戳很符合这个特性,时间戳是从美国时间1970年1月1日00:00时到现在经过的秒数,不考虑闰秒.时间是一直在变的.
  5. 通过观察文档发现:srand()接受的参数是unsigned int类型,而time()返回的是一个time_tlong long int的类型,需要经过强制转换,才能让参数被成功接收.
  6. 在C语言获得随机数的操作如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int main()
    {
         srand((unsigned int)time(NULL));            //定义随机数起点
         int random_num = rand();                    //获得一个随机数
    
         return 0;
    }
    

4. goto语句

C语言提供了可以随意滥用的goto语句和标记跳转的符号.
从理论上goto语句是没有必要的,实践中没有goto语句也可以很容易地写出代码.
但是在需要跳出多层循环的场合下goto还是用得着的.
多层循环中break是达不到目的的,只能跳出本个循环,到上一个循环中.

goto语句真正的适合场景如下

for(...)
    for(...)
        for(...)
            {
                if (disater)
                    goto error;
            }
...
error:
    if (disater)
        //处理错误情况

下面是使用goto语句的一个例子,然后使用循环的实现方式替换goto语句`:
一个关机程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char input[10] = {0,};
    system("shutdown -s -t 60");
again:
    printf("电脑将在60s内关机,如果输入:我是猪,就取消关机!\n请输入:");
    scanf("%s", input);
    if (0 == strcmp(input, "我是猪"))
    {
        printf("好的我知道了,已经取消关机.\n");
        system("shutdown -a");
    }
    else 
    {
        goto again;
    }

    return 0;
}

用循环同样可以做

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char input[10] = {0,};
    system("shutdown -s -t 60");
    while(1)
    {
        printf("电脑将在60s内关机,如果输入:我是猪,就取消关机!\n请输入:");
        scanf("%s", input);
        if (0 == strcmp(input, "我是猪"))
        {
            printf("好的我知道了,已经取消关机.\n");
            system("shutdown -a");
            break;
        }
    }
    return 0;
}

本章完.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值