【程序设计基于C】第二章 C程序的基本控制结构(完结)【期末复习】

本章速览

  • 本章内容:本章主要介绍的是一些常见的运算符(包括关系运算符和逻辑运算符),分支和循环结构。
  • 目标要求:此章节内容为程序设计的核心板块,对于初学者,要求知道各选择、循环结构的写法和规则;对于期末备考的选手,则要求能够熟练(也就是完全不动脑子)的写出自己所需要各种结构。
  • 学习方式:所学习的内容为所有程序语言均通用的内容,若有过学习其他语言的经验,只需快速浏览即可。对于第一次接触程序设计的朋友,需要的则是多多练习,此部分重在实践,只是看书是很难真正掌握的。

以前我能找到的程序刷题网址,大多针对求职选手,对于我这种初学者相当的不友好。某天老师分享一个适合刷题网站,其中的题目难度跨度较大,个人认为适合初学者,在此一并分享给大家。

1. C语言关系运算和逻辑运算

想要知道更多的运算符的使用,可以看上一章中与运算符相关的内容!

1.1 关系运算符

  • 关系运算符:用于两个同类型的数据对象之间比较运算的运算符。

不要被这里的同类型给吓到:当你想用小数和整数进行比较的时候,计算机还是能够正常的比较的。【但是有些时候判断等于可能不成立,需要用二者差值小于一个较小的数表示相等】

常见的关系运算符有:

运算符>>=<<===!=
含义大于大于等于小于小于等于等于不等于

这一部分都是熟悉的老朋友,需要整理的内容并不多,对于新手而言需要注意的是等于的判定是两个等号==,因为在程序语言里单个的等号=表示的是赋值。

除此之外还需要注意的有:

  1. 关系运算符的左结合性和低优先级(低于算术运算符)
  2. 关系运算符运行的结果为一个数【1真0假:反正是一个数】

对于这个知识点最常见的考法就是,关于连续的大小判断,如何正确的表示判断x是否介于58之间:

//正常人的表示方法(正确):
if (x>=5 && x <= 8)
//小萌新的表示方法(错误):
if (5 <= x <= 8)

可能还存在这样一类人:他们知道下边的写法是不可取的,但是并不知道if(5 <= x <= 8)的等价形式是if(1)。那是因为正如上边所说:

  1. 关系运算符会从左往右的进行判断
  2. 并且判断的结果是一个0或者1的值

因此当判断是否<= 8的时候,无论5 <= x输出的结果是0还是1都是恒成立的,所以是一个永真的情况。【不过如果关系运算符满足右结合性的话就会变成永假的情况了】

1.2 逻辑运算符

逻辑运算符包括:与(&&)、或(||)、非(!

逻辑运算符存在许多的坑,比如说:

  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);

运行后的区别在于:后者的areaprintf无论如何都会执行

  • 对于前者:只要if的判定不通过,三行语句都将跳过。
  • 对于后者:即使if的判定不通过,也只跳过第一行语句。

其实应该说:if后边都应该只跳过一句,只是{}里的所有内容被程序当做了一句而已。

除此之外,合理的使用条件运算符?:;也可以使得自己的代码显得更简洁【据说运算也更快】,在这里介绍两个自从我知道过后就挺喜欢用的写法:

  1. 要求循环的最后一个数不输出空格
for(int i = 0; i < n; i++)
    (i = 0)?(printf("%d", i)):(printf(" %d", i));
  1. 要求如果x0时返回0,其它情况返回1
return x?1:0;
//下边是一种我曾经认为可以的错误写法
x?retrun 1: return 0;

2.2 多分支结构

常见的多分支选择结构,可以通过if-else ifswitch语句进行实现;也可以通过双分支语句的嵌套进行实现。

对于单分支或者双分支语句反复嵌套构成的多分支语句,在此不做说明【因为比较简单,而且写出来感觉有点点的不好看,虽然有些时候必须要这样写】;且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语句的时候,已经不会用了,其使用需要注意以下问题:

  1. 表达式只能是整型、字符型、枚举型三者之一;
  2. case后边的语句是单条语句,也可以是多条语句,但是多条语句不构成符合语句,所以可以不加{}
  3. 结构中的常数值需要与表达式对应,且不能相同;
  4. 可以视情况选择breakdefault

值得一提的是: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循环进行一些更加详细的说明:

  1. 韩信点兵问题:用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 whilewhilefor循环,基本上是可以互换的,主要是看哪种方式写起来更加的顺手。就此题而言,个人更喜欢for的写法,因为写while循环的时候会存在一个将事件转化为补事件的过程,不够无脑,不喜欢。

  1. 另类的换行输入:

for循环中的exp3代表的是在每次循环的最后需要执行的语句,合理的运用这个规则,可以简化某些代码,比如下边这样

#include <stdio.h>

int main()
{
    for (int i = 0; i < 5; i++, printf("%d\n", i));
    return 0;
}

另外还需要说的是,通常的for循环后边都是不跟;的,如果出现了;则表明执行的是空语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值