分支语句  if  Switch   循环语句 while for  do while  goto语句

1.什么是语句?

C语言中有5类语句:表达式语句,函数调用语句,控制语句(分支和循环都属于控制语句),复合语句,空语句

控制语句:用于控制程序的执行流程,以实现程序的各种结构方式(C语言是结构化的程序设计语言,三种结构:顺序,选择,循环)C语言中有9种控制语句:

  • 分支语句:if Switch
  • 循坏执行语句:while for  do while
  • 转向语句:break语句,goto语句,continue语句,return语句

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

if语句

语法结构:

单分支:if(表达式)语句;   //表达式为非0(真),语句就要执行,为假就不执行语句

#include <stdio.h>
int main()
{
    if (3 == 5)
        printf("hehe\n"); //表达式为假不执行输出语句
        
    int a = 10;
    if (a > 5)
        printf("haha\n"); //表达式为真执行输出语句
        
    return 0;  
}

双分支:if(表达式)语句1; else  语句2;//表达式为真,执行语句1;表达式为假,执行语句2

#include <stdio.h>
int main()
{
    int score = 95;
    if (score > 85)
        printf("录取\n"); // if语句后默认只能跟一条语句
    else
        printf("不录取\n");

    if (score > 60)
    {
        printf("合格\n"); //跟多条语句用{}(代码块)
        printf("录取\n");
    }
    else
        printf("不录取\n"); //else语句同理,默认只能跟一条语句
                           //跟多条语句用{}(代码块)
    return 0;
}

多分支:if(表达式1)语句1; else if(表达式2)语句2; else 语句3;//那个表达式结果为真就执行那个语句,都为假则执行else下的语句

#include <stdio.h>      //容易出错的地方
int main()
{
    int score = 50;
    if (60 < score < 70) //不能写成这种形式,score=50 60<50 为假:0 0<70 为真 执行语句
        printf("合格\n"); //不在范围却执行输出
    return 0;
}
#include <stdio.h>      
int main()
{   
    int score=0;
    scanf("%d",&score);
    if (score < 60) //多分支(只选择一个分支执行语句)
        printf("不合格\n");  
    else if (score >= 60 && score < 70)
        printf("合格\n");
    else if (score >= 70 && score < 80)
        printf("良好\n");
    else if (score >= 80 && score < 90)
        printf("中等\n");
    else
        printf("优秀\n");
    return 0;
}

代码风格

例题:悬空else

#include <stdio.h>
int main()
{
    //悬空else    
    int a = 0;         //代码风格不好
    int b = 2;
    if (a == 1)
        if (b == 2)
            printf("hehe\n");
    //else与离得最近的一个if匹配,即使写成与最外面if对齐,也不是与最外面的if匹配        
    else                   
        printf("haha\n");
    //所以运行后没有输出内容
    return 0;
}
if (a == 1)        //规范写法
 {                  //这里{ }加了更方便理解,也可以不加(一条语句)
        if (b == 2) // if与else 合起来是一条语句
            printf("hehe\n");
        else        // else与离得最近的if匹配,跟对齐无关
            printf("haha\n");
  }
if (a == 1)
 {
        if (b == 2) 
            printf("hehe\n");//加了{}里面if和else无法匹配
 }                           //else只能与外部if匹配
else 
        printf("haha\n");    //输出haha

注意:

  1. if else两个合起来是一条语句,所以上面那个例题中最外面的if语句下跟一个if else语句,不用加{ }
  2. 代码风格要规范:变量命名要有意义有规范
int a;//代码行数太多,a,b,c这种无意义的命名不方便理解阅读
char firstname//这种写法不规范,可读性不高
char FirstName//下面两种写法更规范,可读性更高
char first_name

推荐书籍: 《高质量C++/C编程指南》

例题:if书写形式对比

//写法1
if(condition){ //条件满足,return x; 
               //条件不满足不会执行语句直接跳到return y;
   return x;   //写法1和写法2意思是一样的,但写法1容易误解,代码风格不好
}
return y;

//写法2
if(condition)
{
   return x;
}
else
{
   return y;
}
//实例1:
int test()
{
    int a = 3;  //不好的写法
    if (a == 3)
        return 1;
    return 0;
}

#include <stdio.h>
int main()
{
    int r=test();
    printf("%d\n",r);
    return 0;
}

修正:
int test()
{
    int a = 3;  //好的写法
    if (a == 3)
        return 1;
    else
        return 0;
}

注意:两种写法是一个意思,但是写法一容易误解,写法二更通俗易懂

例题:=与==(好的代码风格可以避免一些错误)

//实例2:
#include <stdio.h>
int main()
{
    int num = 3;
    if (num == 5) //容易将==比较大小的符号写成=赋值
                  //即num=5,那么表达式一定为真,无论num等不等于5都会执行输出语句
                  //这种错误编译器检测不出来
        printf("hehe\n");

    int num1 = 3;
    if (5 == num1) //写成下面这种方式,如果将==写成=编译器就会报错,避免了一些错误
        printf("haha\n");  //常量与变量比较时,将常量放在左边(变量可以赋值)
    return 0;
}

练习

例题:判断一个数是否为奇数

//判断一个数是否为奇数
#include <stdio.h>
int main()
{   
    int num = 0;
    scanf("%d", &num);
    //判断
    if (num % 2 == 1)     //此处没有必要倒过来写
        printf("奇数\n");  //表达式和常量,表达式变量无法赋值,所以无需将常量放在左边
    else
        printf("偶数\n");
    return 0;
}

例题:输出1~100之间的奇数

//输出1~100之间的奇数
#include <stdio.h>
int main()
{
    //输出1~100之间的奇数
    //产生1~100的数  while
    //判断输出1~100的奇数
    int num = 1;
    while (num <= 100)
    {
        if (num % 2 == 1)
            printf("%d\n", num);
        num++;
    }
    //法2
    int i = 1;
    while (i <= 100)
    {
        printf("%d\n", i);
        i += 2;
    }

    return 0;
 
}

Switch语句

也是一种分支语句,通常用于多分支的情况(if用于多分支不简洁,太复杂)

语法结构

Switch(整型表达式)

{

   语句项;

}

语句项如下:

case  整形常量表达式:

       语句;

注意:整型表达式值结果是整型,据表达式值与语句项进行匹配,语句项又是case表达式

例题:输入数字输出对应星期数

#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:                 //语句项中不加break
        printf("星期三\n"); //输入3会打印输出星期三...星期日
        break;              //只给了程序入口,未告知程序出口,会一直往后走完最后一个语句项停止
    case 4:
        printf("星期四\n");
        break;
    case 5:
        printf("星期五\n");
        break;
    case 6:
        printf("星期六\n");
        break;
    case 7:
        printf("星期日\n");
        break;
    }
    return 0;
}

注意:Switch后面表达式必须是整型不可以是浮点型,case后表达式必须是整型常量,不能是浮点型或者变量,case后面表达式可以跟字符常量(字符存储时存的是ASCII,ASCII本质上也是整数,也属于整型)

例题变形:1~5打印输出weekday  6~7打印输出weekend

#include <stdio.h>
int main()
{
    int day = 0;
    scanf("%d", &day);
    switch (day)
    {
    case 1://case后面可以不跟break语句,没有break语句会持续往下走
    case 2://可以实现多个case语句匹配同一个执行语句
    case 3:                 
    case 4:
    case 5:
        printf("weekday\n");
        break;
    case 6://执行语句相同时可以简化代码
    case 7:
        printf("weekend\n");
        break;//最后一个break貌似可以不加,但是建议加上,以防后面再加case语句出错
    default:
        printf("选择错误\n");
        break;
    }
    return 0;
}

default子句

输入错误没有匹配的case语句项,从default进入并跳出Switch语句,可以给出提示性语句

例题 打印输出的m=?n=?

#include <stdio.h>
int main()
{
    int n = 1;
    int m = 2;
    switch (n)
    {
    case 1:
        m++;
    case 2:
        n++;
    case 3:
        switch (n) // switch允许嵌套使用
        {
        case 1:
            n++;
        case 2:
            m++;
            n++;
            break; //此处的break是从里面的Switch跳出到外面的Switch
        }          //外面的Switch在case 3没有break还会继续往后执行case 4
    case 4:
        m++;
        break;
    default:
        break;
    }
    printf("m =%d,n=%d\n", m, n);// m=5,n=3
    return 0;
}

注意:

  1. Switch是可以嵌套的
  2. Switch中break语句只能跳出自己所在的Switch,不是一个break语句跳出所有的Switch语句

3.循环语句

while语句   for语句   do while语句

while语句

语法结构

while(表达式)//表达式为真,执行循环语句,执行完再判断,直到表达式为假退出循环

       循环语句;

例题:生成1—10的数字

//打印1~10数字
#include <stdio.h>
int main()
{
    int i = 1;
    while (i < 11)
    {
        printf("%d\n", i);
        i++;
    }
    return 0;
}

while循环执行流程:

C语言初阶-分支语句和循环语句_goto语句

先判断表达式,表达式为真,执行循环语句,执行完后再次判断表达式真假,为真就继续执行循环语句,当有一次表达式为假就停止循环

while循环中break和continue

//循环中break和continue的意思
//实例1
#include <stdio.h>
int main()
{
    int i = 1;
    while (i < 11)
    {
        if (5 == i)//只打印1~4
            break; //break是作用于循环的,而不是作用于if语句,所以将if下的break用{}扩起来还是会终止循环 
        printf("%d\n", i);
        i++;
    }
    return 0;
}
//实例2:
#include <stdio.h>
int main()
{
    int i = 1;
    while (i < 11)
    {
        if (5 == i)   // 打印1~4 光标一直在闪动(死循环)
            continue; // continue被执行会跳过本次循环continue后面的代码
                      // 直接到判断部分判断要不要进行下次循环
        printf("%d\n", i);
        i++;
    }
    return 0;
}

//实例2的变形
#include <stdio.h>
int main()
{
    int i = 1;
    while (i < 11)
    {
        i++;         
        if (5 == i)   
            continue; //输出2,3,4 6,7,8,9,10,11
                      //只跳过了5没有输出,执行了continue语句跳过本次循环continue后面的代码,重新判断能否进行下次循环
                      //以后每次循环都不满足5==i,所以continue只执行一次
        printf("%d\n", i);
        
    }
    return 0;
}

注意:

  1. break的作用是终止整个循环,应用场景:某种条件下终止循环
  2. continue的作用是跳过本次循环continue后面的代码,直接到判断部分判断是否进行下次循环,可能会导致代码死循环
  3. 区别:break是用于永久的终止循环,continue是跳过本次循环后面的代码,直接去判断部分,进行下次循环的判断

与while循环有关的代码

代码1

//这里的代码适当修改是可以用來清理缓冲区的.
#include <stdio.h>

int main()
{
    int ch = 0;
    while ((ch = getchar()) != EOF) // getchar()获取字符的ASCII值
        putchar(ch);                //打印字符 方法二printf("%c\n",ch);
    return 0;                       // end of file 文件结束标志(值=-1 整数)
}
// getchar读取出错/遇到文件末尾的返回值为EOF,读取正确返回读取字符
// EOF就是-1  如果定义一个字符型变量(1byte),无法接收存储-1这个返回值
//所以定义一个整型去接收getchar()的返回值
//读到正常字符就打印输出 停止的方法(getchar返回EOF):Ctrl+z

//输入缓冲区存在于getchar和键盘之间的,当输入字符'a'时,敲入回车才会被放入输入缓存区
//因此输入缓冲区有'a''\n'两个字符,getchar依次去读取并进入循环输出
//显示效果是:输出字符后还换行显示光标

输入a 打印输出a 后换行显示光标,说明输入缓冲区的有'a''\n'两个字符

C语言初阶-分支语句和循环语句_分支语句_02

代码1的变形:清理缓冲区

//举一个例子
//假设密码是一个字符串
#include <stdio.h>
int main()
{
    char password[20] = {0}; // password是数组名,数组名本来就是地址所以scanf这里不用加取地址
    printf("请输入密码:");  // abcdef\n(敲了回车键才能到输入缓存区被scanf获取)
    scanf("%s", password);   // scanf只取abcdef放进password,不会获取\n(留在输入缓存区)
     
    //getchar(); //不好的改进:读取\n 使输入缓存区变空(临时写法不是完全正确)
               //当输入abcdef defghi\n时,scanf只会读取空格前的字符
               //所以输入缓存区还剩 defghi\n  getchar()只能读取一个字符 此处getchar读取一个空格
               //输入缓存区中还剩有defghi\n,下一个getchar()直接读取字符,不用等待 打印输出:No
               //此处的加一个getchar()改进代码完全失效

    int ch = 0;//好的改进:清空缓存区
    while ((ch = getchar()) != '\n')//直到读取到最后一个字符\n表达式为假,退出循环
    {
        ;
    }

    printf("请确认密码:Y/N");
    int ret = getchar(); //取走了输入缓存区中的\n
    if ('Y' == ret)      //\n!='Y'执行else语句打印输出 No
    {
        printf("Yes\n");
    }
    else
    {
        printf("No\n");
    }
    return 0;
}

不加清理缓冲区的语句时,直接不要你输入选择就直接输出No

C语言初阶-分支语句和循环语句_分支语句_03

不好的改进,只用一次getchar(),无法应对缓冲区有多个字符的情况

C语言初阶-分支语句和循环语句_分支语句_04

代码2:只打印数字字符,跳过其他字符

//代码功能:只打印数字字符,跳过其他字符
#include <stdio.h>
int main()
{
    char ch = '\0';
    while ((ch = getchar()) != EOF)
    {
        if (ch < '0' || ch > '9')
            continue;
        putchar(ch);
    }
    return 0;
}

输入字符a、q都被跳过了不打印输出,输入数字4,打印输出

C语言初阶-分支语句和循环语句_分支语句_05

输入abcde1234wer,跳过了字符不打印只打印数字

C语言初阶-分支语句和循环语句_分支语句_06


for语句

for循环的形式是将初始化,判断,调整三个部分放在一起,比较集中,容易控制循环。因此for循环用的最普遍,最方便。

语法结构

for(表达式1,表达式2,表达式3)

     循环语句;

表达式1:初始化部分(初始化循环变量) 表达式2:条件判断部分(判断循环何时终止) 表达式3:调整部分(循环条件的调整)

例题:打印1~10

//for循环打印1~10
#include <stdio.h>
int main()
{ //打印1~10
    int i = 0;
    for (i = 1; i <= 10; i++)
    {
        printf("%d", i); //单条语句可以不加{},两条及以上必须加{}
    }
    return 0;
}

for循环执行流程

C语言初阶-分支语句和循环语句_goto语句_07

exp1:初始化部分  exp2: 判断部分  exp3:调整部分

先执行初始化部分,再执行判断部分,判断如果为非0(即表达式结果为真)就执行循环体部分,执行完循环体就进入执行调整部分,调整完之后再进行判断,表达式结果一直为真,就一直这样循环,当有一次判断为假时,就跳出循环。

for循环中的break和continue

#include <stdio.h>
int main()
{
    int i = 0;
    for (i = 1; i <= 10; i++)
    {
        if (5 == i)
            // break;       //跳出循环,终止循环

            continue; //跳过continue本次循环后面的代码来到调整部分
                      //与while中不同,while中的continue时跳过本次循环后面的代码来到判段部分

        printf("%d", i); //输出1,2,3,4 break
                        //输出1,2,3,4,6,7,8,9,10  continue
    }
    return 0;
}

注意:break 永久终止循环(结束所在的循环),continue 跳过本次循环后面的代码直接跳到for循环的调整部分

for语句的循环控制变量

建议:

  • 不可在for循环的体内修改循环变量(在调整部分修改),防止循环失控
  • for语句的循环控制变量取值采用 前闭后开区间 写法

C语言初阶-分支语句和循环语句_循环语句_08

两种写法都可以打印出数组中所有元素,但是更推荐第一个写法(前闭后开区间 )第一个代码10 就是循环次数,可读性更高,写9没有10好,没有意义,只是更推荐使用第一个写法,但在某些场景不适合,eg

#include <stdio.h>
int main()
{ //打印100~200
    int i = 0;
    for (i = 100; i <= 200; i++)//为了可读性更高没有写成前闭后开区间 
    {                           //所以写法一不是绝对的,在一些场景也不适用
        printf("%d\n", i);
    }
    return 0;
}

for循环的变种

变种1:死循环问题和省略

#include <stdio.h>
int main()
{
    //代码1 死循环的问题和省略初始化
    for (;;)
    {
        printf("hehe\n");
        //死循环一直输出hehe,原因是for循环中的判断部分都省略了
    }
    // for循环的判断部分省略意味着判断会恒成立,所以会死循环(初始化和调整部分不影响死循环)
    // for循环中的初始化部分,判断部分,调整部分是可以省略的, 但是不建议初学时省略,容易导致问题
     return 0;
}

省略初始化:

#include <stdio.h>
int main()
{   //省略初始化
    int i = 0;
    for (/*i = 0*/; i < 10; i++)
    {
        printf("haha\n"); //省略后仍然可以打印输出10个haha
    }

    return 0;
}
#include <stdio.h>
int main()
{//省略初始化出问题了
    int i = 0;
    int j = 0;
    for (/*i = 0*/; i < 3; i++)
    {
        for (/*j = 0*/; j < 3; j++)
        {
            printf("gaga\n"); //不省略for循环初始化部分打印9个gaga

            //省略 只打印3个gaga
            //原因是当省略初始化时,先进入外部循环判断部分0<3,执行循环语句
            //进入内部循环判断部分0<3,执行循环语句,循环输出3个gaga后退出内部循环,此时j=3
            //进入外部循环调整部分i++,进入外部循环的判断部分i=1<3,再次进入内部循环判断部分
            //但是此时j=3(无初始化部分,不进行初始化),不满足<3,不进入内循环执行语句
            //此后i=2与i=1同理 i=3退出外循环
        }
    }
    return 0;
}

一种写法的说明:

#include <stdio.h>
int main()
{ //一种写法
    for (int i = 0; i < 10; i++)
    {
        printf("guagua\n"); //也可以正常输出10个guagua
                            // C语言:C99语法中才支持这种写法,不支持C99的编译器无法正常编译
                            // C++:支持这种写法
    }
    return 0;
}

变种2:使用多个循环变量控制循环

#include <stdio.h>
int main()
{ //代码2 使用多个循环变量控制循环
    int x, y;
    for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
    {
        printf("hehe\n");
    }
    return 0;
}

例题:判断下面循环会循环几次

#include <stdio.h>
int main()
{
    int i = 0;
    int k = 0;
    for (i = 0, k = 0; k = 0; i++, k++) //循环0次
                                        //原因:判断部分不是判断语句而是赋值语句
                                        //判断部分将k赋值为0,表示式结果为0,0为假,不进入循环
    {                                   //变形:判断部分 k==0 循环1次
        k++;
    }
    return 0;
}

do while语句

语法结构

do 

     循环语句;

while(表达式);

特点:先执行循环语句再判断表达式(要不要继续进行),循环体至少执行一次

执行流程

C语言初阶-分支语句和循环语句_分支语句_09

先执行循环体语句,再判断表达式真假,表达式为真(不为0),就继续执行循环语句,表达式如果为假就停止

例题:打印1~10

#include <stdio.h>
int main()
{ //打印1~10
    int i = 1;
    do
    {
        printf("%d ", i);
        i++;
    } //循环体中多条语句需要加{}
    while (i <= 10);
    return 0;
}

do while语句中的break和continue

#include <stdio.h>
int main()
{
    int i = 1;
    do
    {
        if (i == 5)
            // break;
            continue;
        printf("%d ", i); // i++放在循环体最后一行 输出:1,2,3,4   break
                          // i++放在循环体第一行  输出:2,3,4    break
                          // break跳出循环,让循环整体终止
                          
        // i++放在循环体第一行 输出2,3,4,6,7,8,9,10,11      continue
        // i++放在循环体最后一行 输出 1,2,3,4 死循环了    continue
        // continue:跳过continue后面的代码,来到判断部分
        i++;
    }

    while (i <= 10);
                      
    return 0;
}

do while语句的特点

循环至少执行一次,使用场景有限,不经常使用(用的多到少:for while do while)

循环的练习题

例题1:计算n的阶乘

#include <stdio.h>
int main()
{ //计算n的阶乘    1*2*3*...*n
    int i = 1;
    int n = 0;
    int ret = 1; //不考虑溢出问题
    scanf("%d", &n);
    for (i = 1; i <= n; i++)
    {
        ret = ret * i;
    }
    printf("%d\n", ret);
    return 0;
}

例题2:计算1!+2!+3!+...+10!

//两层for循环,效果差 每个阶乘都是从1开始计算
#include <stdio.h> 
int main()
{ //计算1!+2!+3!+...+10!
    int i = 1;
    int j = 1;
    int ret = 1;
    int sum = 0;
    for (i = 1; i <= 10; i++)
    {
        ret = 1; //不重新赋值会产生累积值导致结果错误
        for (j = 1; j <= i; j++)
        {
            ret = ret * j;
        }
        sum = ret + sum;
    }
    printf("%d\n", sum);
    return 0;
}

更好的做法

1! 1      2! 1*2=1!*2     3! 2!*3     没有必要重复计算(即:从1开始计算)

#include <stdio.h>
int main()
{ //计算1!+2!+3!+...+10!
    int i = 1;
    int ret = 1;
    int sum = 0;
    for (i = 1; i <= 10; i++)
    {
        ret = ret * i;//利用前面计算的结果 一层for循环,效率更高
        sum = ret + sum;
    }
    printf("%d\n", sum);
    return 0;
}

例题3:在一个有序数组中查找具体的某个数字n

//顺序查找
#include <stdio.h>
int main()
{ //在一个有序数组中查找具体的某个数字n

    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //没有利用数组的有序性

    int k = 7; //要找数字7
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (i = 0; i < sz; i++)
        if (arr[i] == k)
        {
            printf("找到了,下标是:%d\n", i);
            break;
        }
    if (i == sz)
    {
        printf("找不到/\n");
    }
    return 0;
}

更好的做法:

二分法  效率更高(查找一次少一半数据,数据越多效率越高)(二分法的前提条件:有序的)时间复杂度是log2n(n是有序数组中元素个数)eg: 有232个有序数字只查找32次  效率很高     

#include <stdio.h>
int main()
{ //有序数组找具体数字 二分查找法 效率高
    // 1.找中间元素下标mid (找出数组左边元素下标和右边元素的下标 中间元素下标是最左边元素left和最右边元素right下标的平均值)
    // 2.比较,不相等就修改right或者left(修改范围)
    // eg:arr[]={1,2,3,4,5,6,7,8,9,10}中找7
    //(0+9)/2=4 整数除法 中间元素下标为4 arr[4]=5<7(要找的数字7在5的右边) 查找一次,查找范围缩小一半(数据少了一半)
    //右下标不变,左下标变成mid+1 新范围:5~9   (重复1,2步骤直至找到)
    //新中间元素下标 mid=(5+9)/2=7 arr[7]=8>7(要找的数字7在8的左边)  左下标不变 右下标变成mid-1 新范围:5~6
    //新中间元素下标 mid=(5+6)/2=5 arr[5]=6<7(要找的数字7在6的右边) 右下标不变,左下标变成mid+1 新范围:只有下标6了(左右下标重合)
    // arr[6]=7 找到数字7 如果此时arr[6]!=7 就找不到数字7了
    //顺序查找:最坏情况查找10次  二分查找最坏情况是要查找的元素可能位于数组的边缘位置,比如第一个或最后一个位置,或者数组中不存在要查找的元素。
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int k = 7; //要找数字7

    int sz = sizeof(arr) / sizeof(arr[0]);
    int left = 0;
    int right = sz - 1;

    while (left <= right) // left<=right 还有元素进行查找
                          //等号不能漏掉,不然会出错
    {
        int mid = (left + right) / 2;//放在循环内部,要多次求mid
        //left和right非常大,加起来可能会溢出,此时mid就不对了
        //解决:left+(right-left)/2 这种做法就不会越界了
        if (arr[mid] < k)
        {
            left = mid + 1;
        }
        else if (arr[mid] > k)
        {
            right = mid - 1;
        }
        else
        {
            printf("找到了,下标是%d\n", mid);
            break;
        }
    }

    if (left > right) // left>right 没有元素进行查找 找不到了
    {
        printf("找不到了");
    }
    return 0;
}

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

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

int main()
{
    char arr1[] = "welcome to Beijin!";
    char arr2[] = "##################";
    int left = 0;
    int right = strlen(arr2) - 1;
    // strlen求字符串长度(\0前的所有字符)最右侧下标:字符串长度-1
    //第二种做法:int right = sizeof(arr1)/sizeof(arr1[0])-2;
    // sizeof获取变量或类型所占字节数 数组总体占的字节/单个数组所占字节 =数组元素(\0占空间但是不算长度)
    //所以最右侧下标=数组元素-2(减去\0和下标从0开始算要减1)
    while (left <= right) // left<=right 还有元素未替换
    {
        arr2[left] = arr1[left];
        arr2[right] = arr1[right];
        printf("%s\n", arr2);
        Sleep(1000); //延迟1s 使变化肉眼可见
        // Sleep(1000)是Windows系统下的函数,它可以使程序暂停执行一段时间,单位是毫秒。要使用需要包含头文件windows.h。
        system("cls"); // system是一个库函数, 可以执行系统命令
        // system("cls")清空屏幕 要使用这个指令需要包含头文件stdlib.h
        //用了这一行可以在一行显示
        left++; //调整部分 向中间汇聚
        right--;
    }
    printf("%s\n", arr2);
    //最后的结果显示,不写最后哪一行显示后延时1s会被清屏
    return 0;
}

例题5:编写程序实现,模拟用户登录场景,并且只能登陆三次(只允许输入三次密码,如果密码正确则提示登陆成功,如果连续三次都输入错误,则退出程序)

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

int main()
{
    //比较2个字符串是否相等,不能使用==,而应该使用一个库函数: strcmp
    //如果返回値是0,表示2个字符串相等
    //使用这个函数应该包含string.h头文件
    int i = 0;
    char password[20] = {0};
    //假设密码是字符串: abcdef
    for (i = 0; i < 3; i++)
    {
        printf("请输入密码:>");
        scanf("%s", password);               //数组本身就是一个地址所以不用加&
        if (strcmp(password, "abcdef") == 0) //有一个容易犯得错:password==“abcdef"
        {
            printf("登录成功\n");
            break;//跳出循环
        }
        else
        {
            printf("密码错误\n");
        }
    }

    if (i == 3)//登录成功和三次输入错误都会跳出循环所以加个if语句
    //登录成功i<3不会执行语句
    {
        printf("三次密码输入错误,退出程序\n");
    }

    return 0;
}

例题6:猜数字游戏实现

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

void meau()
{
    printf("*************************\n");
    printf("*********1.play**********\n");
    printf("*********0.exit**********\n");
    printf("*************************\n");
}

void game() //电脑产生一个随机数(1~100) 猜数字  反馈:猜大了/猜小了
{
    int guess = 0;
    // srand(100);
    //又出问题了,srand中数值不变,对应随机数也不变  srand中数值变化导致随机数变化 srand中需要一个变化的值 那放一个随机数可以吗? 不可以,不现实
    //时时变化的值:时间  时间获取:时间戳(当前时间相对于计算机起始时间之间的差值 单位:s 只要时间在变化,时间戳就会变化),时间戳是一个变化的整数 满足srand()的需求
    // 时间戳获取:time()  time_t time(time_t *timer) 返回一个时间戳(获取系统时间) 参数是一个指针 可以传一个NULL(空指针 跟整型初始化为0类似 相当于0 (void*) 0) 会返回time_t的值(即时间戳)
    // time_t 是C语言中给的类型 本质上是一个整型类型      srand中参数需要一个无符号整型   所以可以将time()的返回值作为srand的参数  但是需要进行强制类型转换(time_t--->unsigned int)
    // srand((unsigned int)time(NULL));    // srand(),rand()需要加头文件stdlib.h  time()需要的头文件time.h
    //使用time()返回的时间戳作为这个随机数的生成起点的设置
    //这里又有一个问题,当输入比较快时,会出现前后两个随机数相同的现象 解决:在调用rand()生成随机数前用srand()设置随机数起点不需要每次调用game()都重新设置,只需要在整个工程下设置一次即可 因此只需要放入主函数即可
    // 1.生成随机数
    int ret = rand() % 100 + 1;
    //生成随机数的函数 int rand (void);不需要参数,返回类型为整型(返回一个0~RAND_MAX的随机整数) RAND_MAX 0x7fff即32767
    // printf("%d\n", ret);
    //发现每次关闭程序再次运行时生成随机数与上次都一样   解决:使用srand()设置随机数的起点(调用rand函数前使用srand设置随机数的生成器)
    //  srand设置随机数的起点 : void srand(unsigned int seed); 内部需要一个无符号整型数
    //现在又出现一个新问题:需要生成1~100的随机数   解决:rand()%100+1:余数是0~99  +  1----->1~100

    // 2.猜数字
    while (1)
    {
        printf("请猜数字:>");
        scanf("%d", &guess);
        if (guess < ret)
        {
            printf("猜小了\n");
        }
        else if (guess > ret)
        {
            printf("猜大了\n");
        }
        else
        {
            printf("恭喜你,猜对了\n");
            break; //直到猜对跳出循环
        }
    }
}

int main()
{
    srand((unsigned int)time(NULL)); //设置随机数起点
    int input = 0;
    do
    {
        meau();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            // printf("猜数字\n");
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新选择\n");
            break;
        }
    } while (input);

    return 0;
}

在使用rand()前未用srand()设置随机数的起点,发现每次关闭程序重新开始游戏出现的 随机数都是一样的

C语言初阶-分支语句和循环语句_goto语句_10

给srand()一个定值eg 100,200 无法产生变化的随机数

C语言初阶-分支语句和循环语句_分支语句_11

C语言初阶-分支语句和循环语句_goto语句_12

将srand()放在game()中,每次调用都重新设置随机数起点,导致输入太快时前后两次生成随机数相同  解决:将srand()放入 main()只在整个工程中设置一次随机数起点

C语言初阶-分支语句和循环语句_分支语句_13

将srand()放入main()中,生成的随机数就很随机了

C语言初阶-分支语句和循环语句_goto语句_14

需要生成的随机数范围在1~100: rand()%100+1

C语言初阶-分支语句和循环语句_分支语句_15

4.goto语句

随意滥用的有跳转功能的语句  (建议少用goto语句:随意乱跳,打乱程序原有执行流程,容易出错)

用法

#include <stdio.h>
int main()
{
again:
    printf("hehe\n");
    printf("haha\n");
    goto again;//again 是个符号 跳转到符号again所在位置  实现死循环
    return 0;
}

goto语句不能跨函数跳转,只能在同一个函数内部跳转

#include <stdio.h>
void test()
{
again:
}
int main()
{
    printf("hehe\n");
    printf("haha\n");
    goto again; //err 
    return 0;
}

应用场景:终止程序在某些深度嵌套的结构的处理过程

C语言初阶-分支语句和循环语句_循环语句_16

三层for循环用一次goto语句就可以跳转出最外层循环,用break语句要跳转3次才可以。

例题:关机程序,电脑运行起来后一分钟内关机;如果输入:不要关机,就取消关机

用goto实现

#include <stdio.h>
#include <string.h> //strcmp()
int main()
{ //关机程序
    //通过在命令行窗口输入命令关机 shutdown -s -t 60 (-s关机 -t设置时间倒计时关机 60表示60秒内关机)
    // shutdown -a 取消关机  在C语言中用库函数system()来执行系统命令
    char input[20] = {0};
    system("shutdown -s -t 60");
again:
    printf("请注意你的电脑在60秒内关机,如果输入:不要关机,就取消关机\n");
    scanf("%s", input);
    if (strcmp(input, "不要关机") == 0) //比较两个字符串不用==,而是strcmp
    {
        system("shutdown -a");
    }
    else
    {
        goto again;
    }
    return 0;
}

不用goto实现

#include <stdio.h>
#include <string.h> //strcmp()
int main()
{ //关机程序
    //通过在命令行窗口输入命令关机 shutdown -s -t 60 (-s关机 -t设置时间倒计时关机 60表示60秒内关机)
    // shutdown -a 取消关机  在C语言中用库函数system()来执行系统命令
    char input[20] = {0};
    system("shutdown -s -t 60");
    while (1)
    {
        printf("请注意你的电脑在60秒内关机,如果输入:不要关机,就取消关机\n");
        scanf("%s", input);
        if (strcmp(input, "不要关机") == 0) //比较两个字符串不用==,而是strcmp
        {
            system("shutdown -a");
            break;
        }
    }
    return 0;
}