目录
本章速览
- 本章内容:本章主要介绍的是一些常见的运算符(包括关系运算符和逻辑运算符),分支和循环结构。
- 目标要求:此章节内容为程序设计的核心板块,对于初学者,要求知道各选择、循环结构的写法和规则;对于期末备考的选手,则要求能够熟练(也就是完全不动脑子)的写出自己所需要各种结构。
- 学习方式:所学习的内容为所有程序语言均通用的内容,若有过学习其他语言的经验,只需快速浏览即可。对于第一次接触程序设计的朋友,需要的则是多多练习,此部分重在实践,只是看书是很难真正掌握的。
以前我能找到的程序刷题网址,大多针对求职选手,对于我这种初学者相当的不友好。某天老师分享一个适合刷题网站,其中的题目难度跨度较大,个人认为适合初学者,在此一并分享给大家。
1. C语言关系运算和逻辑运算
想要知道更多的运算符的使用,可以看上一章中与运算符相关的内容!
1.1 关系运算符
- 关系运算符:用于两个同类型的数据对象之间比较运算的运算符。
不要被这里的同类型给吓到:当你想用小数和整数进行比较的时候,计算机还是能够正常的比较的。【但是有些时候判断等于可能不成立,需要用二者差值小于一个较小的数表示相等】
常见的关系运算符有:
运算符 | > | >= | < | <= | == | != |
---|---|---|---|---|---|---|
含义 | 大于 | 大于等于 | 小于 | 小于等于 | 等于 | 不等于 |
这一部分都是熟悉的老朋友,需要整理的内容并不多,对于新手而言需要注意的是等于的判定是两个等号
==
,因为在程序语言里单个的等号=
表示的是赋值。
除此之外还需要注意的有:
- 关系运算符的左结合性和低优先级(低于算术运算符)
- 关系运算符运行的结果为一个数【1真0假:反正是一个数】
对于这个知识点最常见的考法就是,关于连续的大小判断,如何正确的表示判断
x
是否介于5
到8
之间://正常人的表示方法(正确): if (x>=5 && x <= 8) //小萌新的表示方法(错误): if (5 <= x <= 8)
可能还存在这样一类人:他们知道下边的写法是不可取的,但是并不知道
if(5 <= x <= 8)
的等价形式是if(1)
。那是因为正如上边所说:
- 关系运算符会
从左往右
的进行判断- 并且判断的结果是一个
0
或者1
的值因此当判断是否
<= 8
的时候,无论5 <= x
输出的结果是0
还是1
都是恒成立的,所以是一个永真的情况。【不过如果关系运算符满足右结合性的话就会变成永假的情况了】
1.2 逻辑运算符
逻辑运算符包括:与(&&
)、或(||
)、非(!
)
逻辑运算符存在许多的坑,比如说:
- 三个逻辑运算符的优先级都是不一样的【可以见优先级表】。
&&
和||
都是从左往右读,但是!
是从右往左读【毕竟不是没个!
的左边都有东西】。
不过大多数的坑都不会影响平时的使用,大家在编写程序的时候还是可以尽情的放飞自我。但是有一点值得注意:当逻辑运算符能够判断真假之后,就会直接返回值。
比如说运行下列函数过后x
的值应该是多少呢?
#include <stdio.h>
int main() {
int x = 1;
x++ > 1 && ++x < 2;
printf("%d", x);
return 0;
}
正确答案是x = 2
,认为x = 3
的朋友可能犯了以下两个错误中的一个:
- 不知道
x++ = 1
:对于这种情况,可以参考我的另外一篇博客《a++和++a的区别》。 - 不知道在判断出
x++ > 1
的值为0
过后就结束了判定:由于与运算(&&
)左边经过判定已经是0
,因此会跳过右边的判断。
如果觉得上一题不够过瘾,可以尝试此题,检测自己是否真正完全掌握了逻辑运算符:
#include <stdio.h>
int main() {
int a = 1, b = 2, c = 0;
a-- || b++ && !c++;
printf("a=%d,b=%d,c=%d", a,b,c);
return 0;
}
输出的结果为:a=0,b=2,c=0
。
2. 分支结构程序设计
所谓分支结构,就是根据当前的条件(或者说是输入参数),选择接下来程序的运行路线。
感觉就像是打游戏的时候推进剧情:不同的选择会导致接下来不同的剧情。
2.1 单分支和双分支结构
单分支结构和双分支结构代表性的语句都是if
,如果有else
就是双分支结构,没有的话就是单分支结构。
关于if
,其实就是if-else
的简化版,我在一道例题中已经进行了解释。因此,此处只谈谈if-else
的使用,其使用方法为:
if(exp)
sentence 1;
else
sentence 2;
表达的意思是,如果满足exp
的条件【exp运算的结果为非0
】,则执行sentence 1
,否则则执行sentence 2
,具体可以通过最基础的返回较大值函数进行理解:
#include <stdio.h>
int main(){
int x,y,max;
scanf("%d %d", &x, &y);
if(x>y)
max = x;
else
max = y;
printf("Max = %d", max);
}
其实单分支和双分支结构本就不难,基本上写过几个程序的朋友都能较为熟练的掌握,但是还有一点需要注意,关于
{}
的使用:
为了让所写的程序占地面积看起来并不那么大,有个丧心病狂的人在写程序的时候喜欢删除一些大括号。但是这样胡乱的操作容易造成一些自己难以发现的错误。通过一个程序进行说明,注意区别一下两个程序:形式上的区别在于有无大括号。
//Code 1:正确的if语句写法
if (a + b > c && a + c > b && b + c > a)
{
s = (a + b + c) / 2;
area = sqrt(s * (s - a) * (s - b) * (s - c));
printf("area = %lf", area);
}
//Code 2:一种错误的写法
if (a + b > c && a + c > b && b + c > a)
s = (a + b + c) / 2;
area = sqrt(s * (s - a) * (s - b) * (s - c));
printf("area = %lf", area);
运行后的区别在于:后者的area
和printf
无论如何都会执行
- 对于前者:只要
if
的判定不通过,三行语句都将跳过。 - 对于后者:即使
if
的判定不通过,也只跳过第一行语句。
其实应该说:
if
后边都应该只跳过一句,只是{}
里的所有内容被程序当做了一句而已。
除此之外,合理的使用条件运算符?:;
也可以使得自己的代码显得更简洁【据说运算也更快】,在这里介绍两个自从我知道过后就挺喜欢用的写法:
- 要求循环的最后一个数不输出空格
for(int i = 0; i < n; i++)
(i = 0)?(printf("%d", i)):(printf(" %d", i));
- 要求如果
x
为0
时返回0
,其它情况返回1
return x?1:0;
//下边是一种我曾经认为可以的错误写法
x?retrun 1: return 0;
2.2 多分支结构
常见的多分支选择结构,可以通过if-else if
和switch
语句进行实现;也可以通过双分支语句的嵌套进行实现。
对于单分支或者双分支语句反复嵌套构成的多分支语句,在此不做说明【因为比较简单,而且写出来感觉有点点的不好看,虽然有些时候必须要这样写】;且if-else if
使用方法和if-else
类似,在此处也不做详细说明,接下来的内容重点说下switch
语句。
不过还是要说一嘴,关于
if-else
的先后顺序是从上到下的,上边的不成立才对下边的内容进行判定,如果觉得不好理解可以看下边一个例子:#include <stdio.h> int main() { int score; scanf("%d", &score); if (score < 60) printf("E"); else if (score < 70) printf("D"); else if (score < 80) printf("C"); else if (score < 90) printf("B"); else printf("A"); return 0; }
当输入
75
的时候,输出的结果会是C
。因为在第一次和第二次的判定都没通过,当通过了第三次判定之后就不会对后边进行判定了。
就个人而言,很少使用switch
语句,因为很少遇到这么散,而且是离散的条件,少数情况只要不是要讨论很多情况,都用if-else if
给糊弄过去了。而不常用的后果就是不熟悉,以至于我在第一次必须要使用switch
语句的时候,已经不会用了,其使用需要注意以下问题:
- 表达式只能是整型、字符型、枚举型三者之一;
case
后边的语句是单条语句,也可以是多条语句,但是多条语句不构成符合语句,所以可以不加{}
;- 结构中的常数值需要与表达式对应,且不能相同;
- 可以视情况选择
break
和default
值得一提的是:switch
依然是从上到下的,不过在没有遇到break
或者}
的时候是不会停止的【这点不同于if-else if
】,于是就会出现这种,让人虎躯一震的代码:
#include <stdio.h>
int main()
{ char c;
int i=0;
printf("Input a character: ");
c=getchar();
switch(c)
{
case '9': i++;
case '8': i++;
case '7': i++;
case '6': i++;
case '5': i++;
case '4': i++;
case '3': i++;
case '2': i++;
case '1': i++;
case '0': printf("It is a digiter %d.\n",i);
break;
case ' ': printf("It is a space.\n");
break;
default: printf("It is other character.\n");
}
return 0;
}
而这种代码能够成立的条件,就是基于switch
在不遇到break
之前会一直执行直到遇到break
或者}
。
3. 循环结构程序设计
可以说整个第二章,分支结构和循环结构,是整个程序设计的精髓。在最开始野路子自学程序的时候,在其他各种知识都搞不清楚,数据类型也不用区别的那么明确(此处怀念一下
matlab
的好)。那个时候就凭借循环和选择的基本原理,依然能够写出一些还算不错(个人认为)的程序。
即使现在系统性的学习c
语言,感觉掌握的最好的部分还算选择和循环,无他,因为这两个部分的应用范围实在是太广了,天天都用能不会吗。
也正是因为循环方面的适用范围很广,因此在此章节不会进行过多的讲述,只会介绍一个大概,而更加详细的介绍会在关于PTA
习题的介绍中进行说明【主要是一下子想不全】。
3.1 while
循环和do while
循环
while
循环的大家都熟悉的,通常在给定限制条件的情况下使用。其要求在满足某个条件的情况下,执行循环,最常见的用法就是迭代。我在会用牛顿迭代法求根的例子进行说明。
而do while
循环的适用范围和while
循环基本相同,唯一有一点不同之处就是:do while
循环是先做了,再进行判定(都加了一个do
)也就是说他至少会运行一次,而while
循环则是先进行判定。下边通过一个例子进行说明:
//本题要求编写程序,计算序列部分和 1 - 1/4 + 1/7 - 1/10 + ...
//直到最后一项的绝对值不大于给定精度eps。
#include <stdio.h>
int main()
{
double sum = 0;
double eps, x;
int i = 1, flag = 1;
scanf("%lf", &eps);
do
{
x = flag * 1.0 / i;
sum += x;
i = i + 3;
flag *= -1;
} while (x > eps || -x > eps); //这里有分号while后边没有
printf("sum = %lf", sum);
return 0;
}
对于新手来说,还需要特别注意一点do while
循环的while
后有分号!!
3.2 for
循环
for
循环则是更为普遍的一种循环,通常在指定循环次数的情况下使用,主要用于遍历,毫不夸张的说,个人认为,for
循环是计算机在枚举法应用上的基础。其主要用法我在《判断素数并求和》的例子中进行了详细的说明,在此不进行过多的阐述。
不过想通过两个例子,对
for
循环进行一些更加详细的说明:
- 韩信点兵问题:用
break
跳出的永真循环
for(exp1, exp2, exp3)
中的exp2
是判定条件,只要判定条件永真,那么这个循环将永远执行下去,直到使用break
让其跳出循环。
#include <stdio.h>
/*
士兵排队报数:
按从1至5报数,记下最末一个士兵报的数为1;
再按从1至6报数,记下最末一个士兵报的数为5;
再按从1至7报数,记下最末一个士兵报的数为4;
最后按从1至11报数,最末一个士兵报的数为10;
请编写程序计算韩信至少有多少兵。
*/
int main()
{
int i;
for (i = 0; 1; i++)
if (i % 5 == 1 && i % 6 == 5 && i % 7 == 4 && i % 11 == 10)
break;
printf("%d", i);
return 0;
}
此题还有一种写法(当然还有很多种写法),可以用while
循环进行操作
#include <stdio.h>
int main()
{
int i = 1;
while (i % 5 != 1 || i % 6 != 5 || i % 7 != 4 || i % 11 != 10)
i++;
printf("%d", i);
return 0;
}
也就是说其实do while
、while
、for
循环,基本上是可以互换的,主要是看哪种方式写起来更加的顺手。就此题而言,个人更喜欢for
的写法,因为写while
循环的时候会存在一个将事件转化为补事件的过程,不够无脑,不喜欢。
- 另类的换行输入:
for
循环中的exp3
代表的是在每次循环的最后需要执行的语句,合理的运用这个规则,可以简化某些代码,比如下边这样
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++, printf("%d\n", i));
return 0;
}
另外还需要说的是,通常的
for
循环后边都是不跟;
的,如果出现了;
则表明执行的是空语句。