C语言分支和循环语句

发布时间:2024年01月10日

分支和循环语句

学习内容

分支语句

  • if

  • switch

循环语句

  • while

  • for

  • do while

goto语句

1. 什么是语句?

C语句可分为以下五类:

  1. 表达式语句

  2. 函数调用语句

  3. 控制语句

  4. 复合语句

  5. 空语句

本次介绍的是控制语句。

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

可分成以下三类:

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

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

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

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

如果你好好学习,校招时拿一个好offer,走上人生巅峰。

如果你不学习,毕业等于失业,回家卖红薯。

这就是选择!这是生活中的分支例子

而在C语言中,分支语句是用来根据条件选择性地执行不同的代码块。常见的分支语句有两种形式:if语句和switch语句。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.1 if 语句

那if语句的语法结构是怎么样的呢?

//单分支
if(condition)
{
    // 如果条件为真,执行这里的代码
}
 
if (condition)
{
    // 如果条件为真,执行这里的代码
}
else
{
    // 如果条件为假,执行这里的代码
}
//如果condition为真,则执行if代码块中的语句;如果condition为假,则执行else代码块中的语句(可选)。


//多分支    
if (condition1)
{
    // 如果条件1为真,执行这里的代码
}
else if (condition2)
{
    // 如果条件1为假,且条件2为真,执行这里的代码
}
else if (condition3)
{
    // 如果条件1和条件2都为假,且条件3为真,执行这里的代码
}
...
else
{
    // 如果以上所有条件都为假,执行这里的代码
}

/*其中,condition1、condition2、condition3等是不同的条件表达式。
如果condition1为真,则执行第一个代码块;如果condition1为假,且condition2为真,则执行第二个代码块;以此类推。
最后,如果所有条件都为假,则执行else代码块(可选)*/

注:多分支结构中,如果前面某个条件为真,后面的条件将不再被检查,即只会执行第一个满足条件的代码块。因此,在编写多分支if语句时,要注意条件的顺序,将最可能满足条件的放在前面。这里的一对 { } 就是一个代码块

代码演示:

#include <stdio.h>
//代码1
int main()
{
    int age = 0;
    scanf("%d", &age);
    if(age<18)
   {
        printf("未成年\n");
   }
}
//代码2
#include <stdio.h>
int main()
{
    int age = 0;
    scanf("%d", &age);
    if(age<18)
   {
        printf("未成年\n");
   }
    else
   {
        printf("成年\n");
   }
}

//代码3
#include <stdio.h>
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");
   }
    
}

解释一下:如果表达式的结果为真,则语句执行。

在C语言中如何表示真假?

0表示假,非0表示真。

2.1.1 悬空else

当你写了这个代码:

#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;
}

当我们把这段代码拷贝到VS编译器中运行时,发现没有结果。原来是else是和它离的最近的if匹配的,VS会自动进行格式优化从而导致了这个问题。所以我们写代码时要适当的使用{}可以使代码的逻辑更加清楚,从而减少难以发觉的小错误。

在这里插入图片描述

代码改正:

//代码风格很重要
#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.2 if书写形式的对比
//代码1
if (condition) {
    return x;
}
return y;

//代码2
if(condition)
{
    return x;       //代码1和代码2运行逻辑和结果是一样的
}
else
{
    return y;
}


//代码3
int num = 1;  
if(num == 5)
{
    printf("hehe\n");
}
//代码4            //代码3和代码4在功能上是等价的
int num = 1;
if(5 == num)
{
    printf("hehe\n");
}

代码2和代码4更好,逻辑更加清晰,不容易出错。

代码2在逻辑上比代码1更好,原因如下:

  1. 可读性更高:代码2使用了明确的else语句,可以更清晰地表达条件的两个分支。对于阅读代码的人来说,更容易理解代码的意图。
  2. 可维护性更高:如果以后需要在条件判断的情况下添加更多的分支,代码2的结构更加灵活和易于扩展。只需添加新的else if分支即可。而代码1在没有else语句的情况下,添加新分支会导致代码结构混乱。
  3. 代码风格统一:代码2使用了大括号包裹条件块,使得代码风格统一。这样可以减少出错的可能性,并且让代码更易于阅读和维护。

代码4使用了常量优先写法,可以避免赋值错误:在代码3中,如果不小心将num == 5写成num = 5,就会将5赋值给变量num,导致条件判断始终为真。而在代码4中,将常量5写在变量num之前,如果意外地写成5 = num,编译器会报语法错误,帮助我们发现并纠正错误。

2.1.3 练习
  1. 判断一个数是否为奇数

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

// 第一种while循环
int main()
{
	int i = 1;
	while (i <= 100)
	{
		if (i % 2 == 1)
			printf("%d是奇数 ", i);	
		i++;
	}
}

// 第二种for循环
int main()
{
	int i = 0;
	for (i = 1; i < 101; i+=2)
	{
			printf("%d\n", i);
	}
	return 0;
}

2.2 switch语句

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

比如:

输入1,输出星期一

输入2,输出星期二

输入3,输出星期三

输入4,输出星期四

输入5,输出星期五

输入6,输出星期六

输入7,输出星期日

那我没写成if...else if ...else if的形式太复杂,那我们就得有不一样的语法形式。这就是switch 语句。

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

语句项是什么呢?是一些case语句:

case 整形常量表达式:
    语句;
2.2.1 在switch语句中的break

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

比如:

#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("输入错误\n");
			break;
   }
    return 0;
}

有时候我们的需求变了:

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

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

所以我们的代码就应该这样实现了:

#include <stdio.h>
//switch代码演示
int main()
{
    int day = 0;
     scanf("%d", &day);
    switch(day)
   {
        case 1case 2:
        case 3:
        case 4:
        case 5:
            printf("weekday\n");
            break;
        case 6:
        case 7:
            printf("weekend\n");
            break;
   }
    return 0;
}

当程序执行到某个 case 分支时,如果没有 break 语句,程序会继续执行下一个 case 分支的代码而不进行判断,这就是所谓的"case 穿透"。break 语句的作用是把语句列表划分为不同的分支部分,同时结束当前 case 分支的执行,并跳出整个 switch 语句。

编程好习惯

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

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

2.2.2 default子句

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

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

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

你可以在语句列表中增加一条default子句。

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

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

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

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

编程好习惯

在每个 switch 语句中都放一条default子句是个好习惯,甚至可以在后边再加一个 break 。

2.2.3 练习
#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;
       }
    case 4:
        m++;
        break;
    default:
        break;
   }
    printf("m = %d, n = %d\n", m, n); //5,3
    return 0;
}

3. 循环语句

  • while

  • for

  • do while

3.1 while循环

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

if(condition)
{
    // 如果条件为真,执行这里的代码
}

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

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

那我们怎么做呢? C语言中给我们引入了: while 语句,可以实现循环。

while (condition) {
    // 循环体语句
}

其中,condition 是一个表达式或逻辑表达式,如果它的值为真(非零),那么就执行循环体语句;如果它的值为假(0),那么就跳过循环体语句,直接结束 while 循环。

在 while 循环中,循环体语句可以是任意合法的 C 语句,包括赋值语句、函数调用语句、条件语句和循环语句等。

while语句执行的流程:

比如我们实现:

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

#include <stdio.h>
int main()
{
 	int i = 1;
 	while(i<=10)
    {
        printf("%d ", i);
         i++;
 	}
 	return 0;
}
3.1.1 while语句中的break和continue
1.break介绍
//break 代码实例
#include <stdio.h>
int main()
{
 int i = 1;
 while(i<=10)
 {
 	if(i == 5)
 		break; // 跳出 while 循环
 	printf("%d ", i);
	 i++;
 }
 return 0;
}

这里代码输出的结果是什么?

1 2 3 4 陷入死循环

总结:

break在while循环中的作用:

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

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

2.continue介绍
//continue 代码实例1
#include <stdio.h>
int main()
{
	int i = 1;
	while (i <=10)
	{
		if (i == 5)
			continue; //跳过本次循环,后面代码不再执行,直接进行下一轮循环,陷入死循环
		printf("%d ", i);
		i++;
	}
	return 0;
}

这里代码输出的结果是什么?

1 2 3 4

//continue 代码实例2
#include <stdio.h>
int main()
{
    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在while循环中的作用就是:

continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,

而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。

再看几个代码:

//代码什么意思?
//代码1
//getchar-->读取一个字符(输入一个字符),返回的是字符对应的ASCLL码值,错误就返回EOF-->-1
//puchar-->打印字符
#include <stdio.h>
int main()
{
 int ch = 0;
 while ((ch = getchar()) != EOF) //实现多组输入,如果要退出循环,按ctrl+c
       putchar(ch);
    return 0;
}
//这里的代码适当的修改是可以用来清理缓冲区的.
//代码2
#include <stdio.h>
int main()
{
    char ch = '\0';
 while ((ch = getchar()) != EOF)
 {
     //ASCLL码除数字部分跳过
     if (ch < '0' || ch > '9') //这个代码的作用是:只打印数字字符,跳过其他字符
        continue;
     putchar(ch);
 }
 return 0;
}

//getchar的应用
int main()
{
	int ch = 0;
	printf("请输入密码:>");
	char pwd[20] = { 0 };
	scanf("%s", &pwd);//scanf输入函数默认读到空格停止  scanf("%[^\n]")直到遇到换行符才停止
	while ((ch = getchar()) != '\n')//getchar清理缓存区
	{
		;
	}
	printf("请确认密码:Y/N ");
	int ch2 = getchar();
	if (ch2 == 'Y')
		printf("密码正确");
	else
		printf("密码错误");
	return 0;
}

什么是缓冲区,scanfgetchar的用法可以参考我之前写的文章,里面有详细介绍:你不知道的scanf函数的那些事CSDN博客

3.2 for循环

我们已经知道了while循环,但是我们为什么还要一个for循环呢?

首先来看看for循环的语法:

3.2.1 语法
for (初始化表达式; 循环条件; 更新表达式) {
    // 循环体语句
}

初始化表达式用于初始化循环变量的。

循环条件是一个表达式或逻辑表达式,如果它的值为真(非零),那么就执行循环体语句。

更新表达式会在每次循环结束后执行,用于更新循环变量的值。

实际的问题:

使用for循环 在屏幕上打印1-10的数字。

#include <stdio.h>
int main()
{
	 int i = 0;
 	//for(i=1/*初始化*/; i<=10/*逻辑表达式*/; i++/*更新表达式*/)
	 for(i=1; i<=10; i++)
 	{
 		printf("%d ", i);
	 }
	 return 0;
}

for循环的执行流程图:
在这里插入图片描述

3.2.2 for循环和while循环对比

现在我们对比一下for循环和while循环。我们先通过两段代码来感受两者的差异。

int i = 0;
//实现相同的功能,使用while
i=1;//初始化部分
while(i<=10)//判断部分
{
     printf("hehe\n");
     i++;//调整部分
}

//实现相同的功能,使用while
for(i=1; i<=10; i++)
{
 printf("hehe\n");
}

使用 while 循环来计算用户输入数字的总和,直到用户输入 0 停止:

total = 0
number = int(input("请输入一个数字(输入0结束):"))

while number != 0:
    total += number
    number = int(input("请输入一个数字(输入0结束):"))

print("总和为:", total)

通过这两段代码的示意,我们不难发现while循环和for循环的一些差异。

第一份代码已经确定的循环次数,采用for循环更加合适,同时也方便修改和查找;而while 循环需要手动设置循环初始值和更新循环变量的语句,相对来说稍显繁琐;

第二份代码处理未知迭代次数的情况时,显然更适合用while循环,无法使用for循环。

因此两者在不同的 场景下和需求下都各自有自己的优势。

主要区别如下:

  1. 初始化和更新:for 循环在语法上允许你在循环开始前进行初始化,并在每次循环结束后进行更新。这使得 for 循环特别适合于需要明确的初始值和递增/递减的循环变量的情况。而 while 循环则没有这种内置的初始化和更新功能,需要手动在循环外部进行设置和更新循环变量。
  2. 迭代次数:for 循环通常用于已知迭代次数的场景,因为它的循环条件是在循环开始前就确定的。例如,你知道需要重复执行某个操作10次,那么使用 for 循环可以更直观地表达这个意图。相反,while 循环通常用于未知迭代次数的情况,因为循环条件可以根据具体的业务逻辑进行动态判断。
  3. 灵活性:while 循环相对更灵活,循环条件可以是任意的表达式或逻辑判断,可以根据具体的需求编写复杂的判断条件。而 for 循环的循环条件通常是一个简单的逻辑判断,更适合于简单的迭代。

总的来说,for 循环和 while 循环各有其优势和适用场景。如果你需要明确的初始化和更新循环变量,并且已知迭代次数,那么使用 for 循环会更方便。而如果迭代次数未知或需要更灵活的循环条件判断,那么 while 循环可能更适合。

3.2.3 break和continue在for循环中

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

但是还是有些差异:

//代码1
#include <stdio.h>
int main()
{
 int i = 0;
 for(i=1; i<=10; i++)
 {
 	if(i == 5)
		 break;  //1234,终止循环
	 printf("%d ",i);
 }
	 return 0;
}

//代码2
#include <stdio.h>
int main()
{
 int i = 0;
 for(i=1; i<=10; i++)
 {
 	if(i == 5)
 		continue; //跳过本次循环,进入下次循环。1234678910
	 printf("%d ",i);
 }
	 return 0;
}
3.2.4 for语句的循环控制变量

建议:

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

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

int i = 0;
//前闭后开的写法
for(i=0; i<10; i++)
{}
//两边都是闭区间
for(i=0; i<=9; i++)
{}
3.2.5 一些for循环的变种
#include <stdio.h>
int main()
{
 //代码1
 for(;;) //初始化,逻辑判断和更新表达式三个部分都可省略,如果逻辑判断部分为空则默认恒为真,则进入死循环
 {
     printf("hehe\n");
 }
    
    //for循环中的初始化部分,逻辑判断和更新表达式是可以省略的,但是不建议初学时省略,容易导致问题。
    
    //代码2
    int i = 0;
    int j = 0;
    //这里打印多少个hehe?
    for(i=0; i<10; i++)
   {
        for(j=0; j<10; j++)
       {
         printf("hehe\n"); //打印100次
       }
   }
    
    //代码3
    int i = 0;
    int j = 0;
    //如果省略掉初始化部分,这里打印多少个hehe?
    for(; i<10; i++)
   {
        for(; j<10; j++)
       {
         printf("hehe\n");//打印10次,没有初始化值,后面j值就变为10了,不满足j<10表达式,就无法打印hehe。
       }
   }
    
     //代码4 使用多余一个变量控制循环
     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++; //循环0次,k=0为假不进入循环
 return 0;
}

3.3 do…while()循环

3.3.1 do语句的语法:
do {
    // 循环体代码
} while (循环条件);

在这里插入图片描述

do-while 循环的执行流程如下:

  1. 首先,执行循环体中的代码。
  2. 然后,检查循环条件是否满足。
  3. 如果循环条件为真(满足),则继续执行循环体,然后再次检查循环条件。
  4. 如果循环条件为假(不满足),则退出循环,继续执行循环后的代码。

需要注意的是,do-while 循环至少会执行一次循环体中的代码,即使循环条件一开始就不满足。这是因为循环体代码会在条件判断之前执行。

3.3.2 do语句的特点

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

#include <stdio.h>
int main()
{
	int i = 10;
	do{
		printf("%d\n", i);
	} while (i < 10);
	return 0;
}
3.3.3 do while循环中的break和continue
#include <stdio.h>
int main()
{
	int i = 10;
	do {
		if (5 == i)
			break;
		printf("%d\n", i);   //打印1234 终止循环
	} while (i < 10);

	return 0;
}


#include <stdio.h>
int main()
{
	int i = 10;
	do {
		if (5 == i)
			continue;
		printf("%d\n", i);  //打印1234 死循环
	} while (i < 10);

	return 0;
}
3.4 练习
  1. 计算 n的阶乘。
  2. 计算 1!+2!+3!+……+10!
  3. 在一个有序数组中查找具体的某个数字n。(前提是有序,讲解二分查找)——会单独讲解.
  4. 编写代码,演示多个字符从两端移动,向中间汇聚。
  5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则

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

3.4.1 练习参考代码:

1.计算N的阶乘

int main()
{
	int i = 0;
	int ret = 1;
	int n = 0;
	scanf("%d\n", &n);
	for (i = 1; i <= n; i++)
	{
		ret *= i;
	}
	printf("%d\n", ret);
	return 0;
}

2.计算 1!+2!+3!+……+10!阶乘相加的和

//第一种写法
int main()
{
	int sum = 0;
	int factorial = 1;
	for (int i = 1; i <= 10; i++) {
		factorial = 1; //一定要进行初始化,否则ret的值会是上个i的连乘结果
		for (int j = 1; j <= i; j++) {
			factorial *= j;
		}
		sum += factorial;
	}
	printf("1!+2!+3!+...+10! = %d\n", sum);
}


//第二种写法,基于第一种写法,通过在外层循环中计算阶乘并将其累加到总和中,可以避免使用内层循环来计算阶乘
int main()
{
	int sum = 0, factorial = 1;
	for (int i = 1; i <= 10; i++)
	{
		factorial *= i;
		sum += factorial;
	}
	printf("1! + 2! + 3! + ... + 10! = %d\n", sum);
	return 0;
}

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

int main()
{
	char arr1[] = "welcome to bit!!!";
	char arr2[] = "*****************";
	int left = 0;
	int right = strlen(arr1) - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000);//暂停1秒
		system("cls");//清空
		left++;
		right--;
	}
	printf("%s\n", arr2);
	return 0;
}

5.编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。

注:当比较字符串时,不能使用 == 运算符(当然其他编程语言是可以的,C语言不行),因为它只会比较两个字符串的地址是否相等。应该使用 strcmp() 函数来比较两个字符串的内容。

它的语法如下:

int strcmp(const char *str1, const char *str2)  //头文件 string.h

参数:

  • str1:要进行比较的第一个字符串。
  • str2:要进行比较的第二个字符串。

返回值:

  • 如果 str1str2 相等,则返回 0。
  • 如果 str1 按字母顺序小于 str2,则返回一个负整数。
  • 如果 str1 按字母顺序大于 str2,则返回一个正整数。

了解完后,具体实现如下:

#include<string.h>
int main()
{
	int i = 0;
	char password[20] = { 0 };
	for (i = 0; i < 3; i++)
	{
		printf("请输入密码:");
		scanf("%s", password);//数组不需要&地址符
		if (strcmp(password, "123456") == 0) //strcmp函数比较内容,单纯的==是比地址
		{
			printf("密码正确,登陆成功!\n");
			break;
		}
		else
		{
			printf("密码输入错误,请重新输入!!!\n");
		}
	}
	if (i == 3)
		printf("三次均错误,退出程序\n");
	return 0;
}
3.4.2 二分查找算法

比如我买了一双鞋,你好奇问我多少钱,我说不超过300元。你还是好奇,你想知道到底多少,我就让你猜,你会怎么猜?

答案:你每次猜中间数。这种思想也叫做折半查找。

二分查找(Binary Search),也称为折半查找,是一种在有序数组或有序列表中查找目标值的算法。它的基本思想是将目标值与数组或列表的中间元素进行比较,通过每次排除一半的数据,逐步缩小搜索范围,直到找到目标值或确定目标值不存在。

二分查找的前提是待查找的数组或列表必须是有序的。具体的算法步骤如下:

  1. 初始化左右边界,左边界为数组的起始位置,右边界为数组的末尾位置。
  2. 计算中间位置 mid,mid = (left + right) / 2。
  3. 将目标值与中间位置的元素进行比较:
    • 如果目标值等于中间位置的元素,则找到目标值,返回该位置。
    • 如果目标值小于中间位置的元素,则更新右边界为 mid - 1,继续在左半部分进行查找。
    • 如果目标值大于中间位置的元素,则更新左边界为 mid + 1,继续在右半部分进行查找。
  4. 重复步骤 2 和步骤 3,直到找到目标值或左边界大于右边界,表示目标值不存在。
  5. 为了方便大家理解,下图演示了查找22值的过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二分查找代码如下:(注:当然也可以直接暴力for循环也可以求解,这里不做代码演示。)

int main()
{
	int arr[] = { 13,15,18,22,28,34,56,65,70,80 };
	int k = 22; //要查找的那个数字
	int sz = sizeof(arr) / sizeof(arr[0]);  //求出arr数组的长度
	int left = 0;
	int right = sz - 1;
	int flag = 0; //找到k值的标志

	while (left<=right)
	{
		int mid = (left + right) / 2;  //当 left 和 right 都是很大的正数时,它们的和可能会超过整型范围,造成溢出
        // int mid = left + (right - left) / 2; //可以使用该表达式防止溢出
		if (arr[mid] == k) {
			flag = 1;
			printf("找到k的值,下标为:%d\n", mid);
			break;
		}
		else if (arr[mid] > k) { //说明K值在mid的左边
			right = mid - 1;
		}
		else {
			left = mid + 1;
		}
	}
	if (flag==0) {
		printf("k值不在该数组中");
	}
	return 0;
}

// 实现一个二分查找函数,这个可以忽略
int bin_search(int arr[], int left, int right, int key)
{
	int mid = 0;
	while (left <= right)
	{
		mid = (left + right) >> 1;
		if (arr[mid] > key)
		{
			right = mid - 1;
		}
		else if (arr[mid] < key)
		{
			left = mid + 1;
		}
		else
			return mid;//找到了,返回下标
	}
	return -1;//找不到
}
3.4.3 猜数字游戏实现

我们在进行猜数字之前,需要实现随机数的产生,那在C语言中怎么实现呢?这就要用到rand函数了,我们先去C/C++官方文档网站(https://legacy.cplusplus.com/reference/ )查看它的使用用法。

在这里插入图片描述
通过看rand官方文档,我们可以看到rand随机数范围是0-RAND_MAX(32767),而我们在使用的过程中rand生成的并不是真正的随机数,而是伪随机数,因为它们是通过一个确定性的算法产生的,并且可以在相同的初始条件下再现相同的序列。为了避免每次程序运行时都生成相同的伪随机数序列,我们通常会使用 先srand() 函数来初始化种子。srand() 函数会将一个初始值设置为种子,这个种子可以是任意的,例如可以基于当前的时间戳来设置。通过每次使用不同的种子来初始化,我们可以得到不同的伪随机数序列。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
time返回的类型是time_t,同时time还需要传入一个time_t类型的指针,我们直接传NULL就好。而srand需要传入的参数是unsigned类型,所以需要强制类型转换。

好了了解完毕后,就可以实现我们的猜数字游戏了。

//猜数字游戏
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void menu()
{
	printf("**********************************\n");
	printf("*********** 1.play ***************\n");
	printf("*********** 0.exit ***************\n");
	printf("**********************************\n");
}
void game()
{
	//1.设置生成1-100的随机数
	int ret = rand()%100+1; //rand是0-32767,在此之前需要srand生成随机生成器种子
	//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()
{
	srand((unsigned)time(NULL));//种子伪随机数生成器,
	int input = 0;
	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;
}

4. 转向语句—goto语句

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

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

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

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

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

注意:goto语句只能在一个函数范围内跳转,不能跨函数。

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

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
	char input[20] = { 0 };
	system("shutdown -s -t 60");//系统关机命令,计算机等待一分钟后自动关机
again:
	printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
	scanf("%s", input);
	if (0 == strcmp(input, "我是猪"))
	{
		system("shutdown -a");//取消关机
	}
	else
	{
		goto again;
	}
	return 0;
}

而如果不适用goto语句,则可以使用循环:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
int main()
{
	char input[10] = { 0 };
	system("shutdown -s -t 60");
	while (1)
	{
		printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
		scanf("%s", input);
		if (0 == strcmp(input, "我是猪"))
		{
			system("shutdown -a");
			break;
		}
	}
	return 0;
}

注:你可以把这个打包成一个小程序发给你的好朋友电脑上捉弄他,哈哈。那还是蛮有趣的。

关于shutdown命令的扩展-(请点这里)

文章来源:https://blog.csdn.net/qq_69218005/article/details/135360501
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。