章节摘要:C语言中有着许多的算法逻辑和思想,它们总是再不断更新和完善,本章节重点介绍C语言算法逻辑中,最为基础也最核心的算法之二,我将以C语言语句的角度去介绍它们:C语言的分支语句,C语言的循环语句
在正式开始之前,需要介绍什么是C语言的语句。
语句,通常指代一些携带信息的信号,用于传达或记录其中的信息,语句中的信息可以是携带对某种事物的表述,也可以是某种既定的指令,也可以是情感的表达等。C语言中的语句,更多指代在人与计算机的语言交互中,人类主观向计算机下达的指令,实际上,就连计算机应对指令所传达的信息,也都是人为主观用一系列指令规定的。可以说,语句基本是人机交互的行为支撑。
C语言常识补充:
1,C语言拥有三大语言结构
I; 顺序结构
II; 选择结构
III; 循环结构
2,C语言语句以分号 ( ; ) 结束。
3,C语言逻辑 或 与 非
逻辑或 —— || 或者
逻辑与 —— && 并且
逻辑非 —— ! 非
4,C语言中的真与假
0 —— 假
!0 —— 真
只要不是0,就是真,C语言的真假明显,真就是真,不真就是假。
C语言中虽然规定只要不是 0 就是真,但是默认 真 都是 1
return,返回,满足返回条件以后就结束语句,程序不会往下执行,会直接跳出所在函数,如果不满足,才往下继续,实际上关于 return 返回的逻辑,涉及到 函数栈帧的创建与销毁。
关于 函数栈帧的创建与销毁 将会在后续内容进行介绍
else,其他分支,else 拥有一定对应原则,在程序的编译中,理清分支归属很重要
else 的对应原则,服从就近原则,对应到与之最近的一个 if 或 if else。
第一节,分支语句
C语言中的分支语句,包含 if 语句,switch 语句。
两者基本都满足成立条件就能指令该分支下的语句,主要区别在于它们的服务对象,当需要考虑的情况复杂多样,采用 if语句 将是效率低下的,尽管可以使用 else 来扩充分支,也很难实质的提示效率,此时 switch 就会非常合适,只需要一套全面的判断逻辑,就能重复去选择,从语句效率和篇幅的角度来说 switch 语句更适合处理复杂多变的情况。而当需要处理的情况很少,使用 if 则会更加直接方面快捷。
1.1 if 语句
if ,中文直译就是 " 如果 ",if 语句 包含两类, if 和 else if
else if 实际上就是 一个额外分支, 根据需要考虑的情况可以适当增加 else if 分支, 但是当条件过多时,应当着重考虑 使用 switch 语句。
下面给出 :if 语句基本框架
if( 成立条件1 )
//当条件成立才会执行这些
{
语句列表
}
//当上述条件不被满足
else if( 成立条件2 )
{
语句列表
}
//当所有预设条件不被满足
else //其实这个 else 可以不写,这里只是为了强调它们的对应关系
{
语句列表
}
下面给出一个十分简单的小例子,该例子稍微结合了 分支语句 中的 if , 循环语句中的 for
循环语句 for :满足条件会持续循环语句列表,后续会介绍
eg 1.1 : 打印并统计 1 - 100 之间的奇数情况
#include<stdio.h>
int main()
{
int num = 0;
int count = 0;
for (num = 0; num <= 100; num++)
{
if (1 == num % 2) // num % 2,当num为偶数,将会得0
{ // 99 % 9 = 0, 99 / 9 = 11
count++;
printf(" %-3d", num);
if (0 == count % 10)
{
printf("\n"); //每打印 10 个奇数,换行
}
}
}
printf("1-100的奇数有 %d 个\n", count);
return 0;
}
输出结果展示:
小小tips:
1,在编译过程中,声明变量的时候,最好顺便初始化它们,实际上如果不影响程序,初始化为任意合适的量都行,但最好统一初始化为0,当然不一定0总是最合适的。
2,在对 if 的语句成立条件进行编译时,推荐采用 上文的形式, 这样做虽然只是一个很小的优化习惯,却能很好避免成立条件编译时出现一些认为的操作失误。
以上就是对 if 语句的一个基本介绍,本章重点在于介绍 它们的基本信息和基本使用,并不深入探讨它们在实际效用中的一些注意事项
1.2 switch 语句
当需要判断的条件太多时,使用 if else 语句编写将会过于冗杂,可采用 switch 语句来进行编写,以满足更大更多的选项
switch 语句基本框架
switch(变量)
{
case 1:
语句列表
break;
case 2:
语句列表
break;
...
...
...
case n:
语句列表
break;
//当所有入口条件都不悲满足,就可以加上 default 语句。
default:
语句列表
break;
}
其中 case x, 表示 箱入口x : 把程序中的变量从这个入口引入使用
注;一旦从某一个窗口引入使用后,如果后面没有break语句来从箱中引出,则会继续往下一个箱入口引入。也就是说,程序本箱会继续从下一个入口在进入下一个箱
可以这样来理解: 整一个 stitch 就像一个仓库,每一个case,相当于一个房间,假设程序需要使用这个仓库,那么当满足case,就进入这个房间,当这个房间中有break,则程序直接退出仓库,如果这个房间中有continue,则继续前往下一个房间。
注;只能前往去过房间的下一个房间。
我绘制了switch的参考图,如下:
图解说明;设定了case 1 2 3 4.
case 1 含有break,程序满足case 1 之后直接跳出仓库。
case 2 含有 continue , 程序 满足 acse 2 之后会进入 case 3中,然后因为case 3 中没有break或continue,将会继续进入 case 4.
case 3 不含有 break 或 continue,程序满足 case 3 之后进入空白房间,然后进入 case 4 ,最后再通过 case 4 的空白房间退出
case 4 不含有 break 或 continue, 程序满足 case 4 之后 将会直接跳出
如果 1 ~ 4 都不被满足,程序将会进入 defalut ,直接跳出仓库
其中 break, 表示 箱出口 : 把程序中的变量从引入的箱中引出,跳出分支选择。
其中 default,表示 不满足: 把程序中的变量引入使用
在这里再简单举例一个 switch 小小例子
eg1.2 :键盘输入停车位,选择车辆
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
switch (a)
{
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;
default:
printf("没有对应车辆\n");
break;
}
}
return0;
当输入的数字是1-6的时候,可以正常输出对应车辆,输入其他的字符或数字将会不显示。
如果把所有的 break 去掉,那么 ,当输入 1 的时候,就会把1-7的车辆都输出,当输入 3 的时候,就会把3-7的车辆都输出,一此类瑞。当输入的字符或者数字不是1-7,将会显示 “没有对应车辆”
注意,case 后的表达式,只能是整形常量表达式。类似于 1.0,2.5等都不行
可以将多个入口并入一个语句列表
swtich中的break不是必须添加,如果想要多个入口条件都指向一个出口,就可以只用一个出口,也可以没有出口,但是这样意义不大default后面也不是必须加上 break。default写在 swtich中的任意位置都是可行的,加不加break都不影响程序正常运行。
只是说,break语句为该分支语句带来了出口,当满足对应的出口条件,程序就能跳出该分支语句。没有break语句程序就会找到从对应入口进入并且走完整个分支语句,如果没有对应入口,则会跳过全部分支语句
注; switch 可以嵌套使用,一个switch语句中可以在嵌套一个或多个switch
小tips:当全局变量有不止一个的时候,应当明确每一个switch的判断变量,哪一个变量用于哪一个分支条件,要明确清晰。同样,哪一个入口的出口break也要判断清楚
第二节:循环语句
C语言中,给循环语句提供了三种循环模式,分别是:
I : while 循环
II : do while 循环
III : for 循环
这三类循环的基本逻辑都是满足条件就会重复执行语句,这意味着,如果没有适当的去设置终止条件,会使得程序进入死循环。
2.1 while 循环
while,中文直译就是 “ 当 ”,也就是循环语句判断条件
下面给出 while 语句基本框架
while (条件)
{
语句列表
}
下面将以一个简单的小例子,来简单的探讨一下:
关于 while 循环, break , continue 之间的一些联系。
eg2.1 将 1 - 100 之间的 偶数打印出来
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int a = 1;
int count = 0;
while (a <= 100)
{
if (0 == a % 2)
{
printf(" %d\n", a);
count++;
}
a++;
}
printf("1-100中的偶数个数为 %d", count);
return 0;
}
输出结果如下:
同样的,类似于 1.1中的 switch语句, break也可以用于 while语句,作用也是跳出,如上图想要输出1-77之间的偶数,并且只统计1-77的偶数个数,就可以用到break
只需要在if里面再次嵌套 if就行
如下面程序,设定当 a >= 77 就会跳出结束循环
#include<stdio.h>
int main()
{
int a = 1;
int count = 0;
while (a <= 100)
{
if (0 == a % 2)
{
printf(" %d ", a);
count++;
}
a++;
if (0 == count % 10)
{
printf("\n");
}
if (a >= 77)
break;
}
printf("\n1-77中的偶数个数为 %d", count);
return 0;
}
输出结果如下
将上面程序中的
break; 改成 continue:
if( a >= 77 )
{
break;
}
//替换为
if (a >= 77)
{
continue;
printf(" continue 没有跳过这一步!\n");
}
,会得到结果
可见,在while循环中,break的作用是跳出循环,永远终止循环,但是在switch中不会结束全部程序。相比于 break 结束循环, continue 则是结束循环的某一次,然后回到循环的判断,继续剩下次数的循环(例如上述程序需要执行38次循环),而且continue会跳过该次循环剩下的语句,才进入下一次循环
getchar
下面是一些关于 getchar 的小介绍
getchar —— 获取一个字符 —— getchar的返回值是 int 整形
int a = getchar ();—— 获取一个字符,并以整形方式赋值给a
getchar 获取字符的方式 :
方式 1 : 从一个文件内获取 方式 2 : 从键盘输入一个
putchar —— 输出一个字符
putchar(a) —— 输出一个字符a
EOF —— end of file —— 文件结束标志 —— EOF = -1
Ctrl + Z —— 让getchar读取到 EOF,从而结束读取,有些编译软件上面是 Ctrl + C
在getchar读取的时候,会在 getchar 与 键盘之间出现一个缓冲区,从键盘输入字符后,通过回车 Enter ,将这个输入的字符放到缓冲区里面,并在后面加上一个 \n。而getchar在读取的时候,会先读取字符A,之后再回来读取 \n,这样在输入下一个字符之前,输入的上一个字符就已经输出并且换行
getchar 还可以起到清理缓冲区,让程序重新进入等待的功能。
当之前输入了一些字符串的时候,会产生 字符串+\n,字符串被使用后,还剩下\n留在缓冲区,此时程序会再次读取\n,从而不会等待下一步操作。如果在操作语句之后单独加上一句
getchar();
就可以把缓冲区的\n也一并清除掉,这样程序再次读取缓冲区的时候,由于没有东西,就会进入等待。
如果需要清理的东西太多,一个 getchar 清理不完,则可以借助while循环语句或者其他循环语句来清除
方式如下:
int tmp = 0;
while((tmp = getchar()) != '\n' )
{
;
}
如此,就可以优先把所有缓冲区里面的东西都清除掉
2.2 for 循环
for循环语句基本框架
for( 表达式1 ; 表达式2 ; 表达式3 )
{
循环语句;
}
其中 表达式1 的作用是 初始化
表达式2 的作用是 判断
表达式3 的作用是 调整
举例 :
int a = 0;
for ( a = 1; a <= 10; a += 2)
{
printf("%d",a);
}
这个程序先定义 a = 0 为初始值,在for循环里面,第一次循环时把a的值 初始化 为 a = 1,然后判断 a 是否满足 <= 10 , 如果满足,就进入进入循环,执行 打印 。然后再回到 调整,执行 a += 2 (把a+2 再次赋值给a)。此hi结束第一次循环,之后程序由回到for循环的初始化环节,开启第二次循环,以此类推,一直执行到 判断 a 时,结束for循环。并且把每一次循环得到的a值(是每一次循环中 a 被初始化以后的值)打印出来。
所以上述简单程序将会打印输出 1 3 5 7 9
如果在上述程序中加入一个 continue
int a = 0;
for (a = 1; a <= 10; a += 2)
{
if (5 == a)
{
continue;
}
printf("%d", a);
}
输出结果就变为了 1 3 7 9,可以发现,当 a=5的时候,满足if条件,continue跳出本次循环,不执行printf打印操作,因此a = 5并没有被打印输出,但是for循环并没有被i终止,程序会回到本次循环的调整环节,此时 a = 7。所以在下一次循环的判断环节之前,a的值就已经被初始化为 a = 7.
综上可以发现,在for语句中,每次循环有三个环节,continue 作用于 判断环节,而且跳出循环时,也只是跳过了该环节的操作,程序跳过该环节以后,还是会在同一次循环中进入下一个环节,即调整环节
for( ; ; ) 的时候,程序进入死循环,因外当判断环节被省略,那么程序每次进入判断环节的时候,恒定判断为真(省略/空,默认为真),故而一直循环下去
当for循环只省略初始化环节的时候,程序采用全局变量中的初始值来进入循环,由于没有进行初始化,变量的值将会保持最后一次循环中的调整环节调整的值
2.3 do while 循环
和 while 循环的区别在于, do while 循环是先编译循环内容,再编译循环条件,并不会说是 do while 会先执行一次循环在判断是否执行下一次循环
do while 语句 基本框架
do
{
循环语句
} while (成立条件)
do while 循环 的逻辑与 while 循环基本一致。
二分查找
利用 while 循环来实现一个C语言中的查找算法: 二分查找
二分查找,又名折半查找,旨每次都选取中间的数据与被查找的数据进行比较,根据情况向小或者大的方向去减半,直到找到 数据 或者 不满足循环条件结束循环。
二分查找基本框架
int left = 0; —— 定义左端位置,数组起始位置
int right = sz - 1; —— 定义右端位置,数组末尾位置 ———— 解释 3
while( left < right ) ———— 解释 1
{
int mid = ( left + right ) / 2; —— 定义中间位置
if ( arr[mid] < k ) —— 假设需要查找的元素是k
{
left = mid + 1;
}
else if ( arr[mid] > k )
{
right = mid - 1;
}
else
{
printf("找到了,位置为%d", mid);
break;
}
}
if( left > right) ———— 解释2
printf("没有发现目标")
}
代码解释:
解释1 ;
设定左端位置小于等于右端,是因为程序识别数组元素是从左到右(从0开始),而二分查找的时候,在确定中点位置以后,需要进行元素比较,以确定端点移动方向,从而,左右端点像目标区域靠拢,相向而行。
解释2 ;
在左右端点相向而行的时候,如果目标元素存在于被查找数组,则会在两端点相遇的时候最终确定出目标元素的确切位置,但是一旦两端点在循环的时候穿过了对方,就表明该数组中没有目标元素
解释3 ;
设定右端为 sz - 1,是因为数组的位置从0开始计,n个元素的数组,元素位置为 0 ~ n-1. 而 sz , 就是数组的元素个数,即为 数组大小 比上 数组单个元素大小
sc = sizeof ( arr ) / sizeof ( arr [0] )
至此,关于 C语言初步的 分支与循环 就暂告一段落。