以下为个人学习笔记和习题整理
课程:计算机程序设计(C++)- 西安交通大学 @ 中国大学 MOOC
https://www.icourse163.org/course/XJTU-46006

# 课堂笔记

# 程序流程控制

# 结构化程序设计

  • 目标
    设计出结构清晰、可读性强、易于分工合作编写和调试的程序。
  • 步骤
    • 自顶向下分析:把复杂问题分解成若干小问题以后再解决
    • 模块化设计:将程序划分为若干个模块,每个模块独立存放、完成一个特定功能。
    • 结构化编码:使用基本控制结构控制程序的执行流程。

# 模块

模块组成:可以是一条语句、一段程序、一个函数等
基本特征:仅有一个入口一个出口
模块间关系:相互独立,内聚性很强

graph TD
  A[C程序] --> B[源程序文件1]
  A --> C[源程序文件2]
  A --> D[源程序文件n]
  
  C --> E[预处理命令]
  C --> F[全局变量声明]
  C --> G[函数1]
  C --> H[函数n]
  G --> G1[函数首部]
  G --> G2[函数体]
  G2 --> I[局部变量声明]
  G2 --> J[执行语句]

# 基本控制结构

graph TB
    subgraph 循环结构
	g --> f
	f(条件?) --> |成立| g[语句序列]
	f --> |不成立| n[ ]
	end
    subgraph 选择结构
    c(条件?) --> |成立| d[语句序列1]
	c --> |不成立| e[语句序列2]
    end
	subgraph 顺序结构
    a[语句序列1] --> b[语句序列2]
    end

语句序列可以是一条语句或者复合语句

# 顺序结构中使用的语句

  • 说明语句
  • 赋值语句
  • I/O 语句输入输出语句
  • 复合语句和空语句
// 复合语句
{
	<局部数据说明部分>
	<执行语句段落>
}
// 嵌套复合语句
{
	<局部数据说明部分1>
	<执行语句段落1>
	{
		<局部数据说明部分2>
		<执行语句段落2>
	}
}
// 空语句
;

# 其他控制结构中使用的语句

  • 流程控制语句

    • 选择
      • 条件分支 if else
      • 开关分支 switch case
    • 循环
      • while
      • for
      • do while
  • 辅助控制语句

    • break
    • continue
    • goto
    • return

# 例子:画出流程图

模拟分拣机对 n 个产品的分拣过程,能够根据产品直径对产品分级并送入不同通道。
具体要求如下:

  • 直径小于 6cm 为三等,送入通道 3;
  • 直径在 6~8cm 为二等,送入通道 2;
  • 直径大于 8cm 为一等,送入通道 1。

流程:

  • 对于多个产品处理:循环结构
  • 对于不同级别的产品处理:选择结构(一等、二等、三等)
  • 对于分拣过程的处理:顺序结构(输入产品 ➡️ 测量直径 ➡️ 判断等级 ➡️ 送入通道 ➡️ 输出产品)

# 单路和双路分支

  1. 单路分支语句
if(a<b)
	max=b;
  1. 双路分支语句
if(a<b)
	max=b;
else
	max=a;

表示条件的表达式要能判断真假,如 a>0 , a%2==0
当语句序列仅包含一条语句时,可以省略花括号

  1. 关系表达式作为条件

  2. 分支嵌套

if(表达式1) 语句1
else if(表达式2) 语句2
else if(表达式3) 语句3
else if(表达式4) 语句4
else 语句5
if(表达式1) { 
	if (表达2) 语句1
} else 语句2 (内嵌if)

else 总是与它上面最近的 if 配对
如果 if 与 else 的数目不一样,可以加花括弧来确定配对关系

# 多路分支

switch (表达式)
{
	case 常量表达式1:
		语句序列1;
	……
	case 常量表达式n:
		语句序列n;
	[default:
		语句序列n+1;]
}

# 例子:成绩转换

编写程序,将一个输入的百分成绩经过运算得到相应的 5 分制成绩。
转换前后的成绩对应关系如下:

90~1005
80~894
70~793
60~692
60 以下1
#include<iostream>
using namespace std;
int main()
{ 
	int old_grade, new_grade;
	cin >>old_grade;
	switch (old_grade/10)
	{
		case 10:
		case 9: new_grade = 5;break;
		case 8: new_grade = 4;break;
		case 7: new_grade = 3;break;
		case 6: new_grade = 2;break;
		default: new_grade = 1;
	}
	cout <<new_grade;
	return 0;
}
  1. 整型表达式作为运算表达式 switch (old_grade/10)

  2. 分支描述

case 10:
case 9: new_grade = 5;break;
case 8: new_grade = 4;break;
case 7: new_grade = 3;break;
case 6: new_grade = 2;break;
  1. 缺省分支描述
default: new_grade = 1;
  1. 中断语句 break

# 已知次数循环

for(表达式1; 表达式2; 表达式3)
{
	语句序列
}
// 表达式 1:< 变量 >=< 初始值表达式 >
// 表达式 2:表示循环条件
// 表达式 3:表示增量
  1. 表达式之间的 ; 不能省略
  2. for(;;) 在语法上是正确的,表示死循环
graph LR
	A[计算表达式1] --> B{表达式2 ?}
    B --> |不成立| D[ ] 
	B --> |成立| C[语句序列] --> E[计算表达式3] --> B

# 例子:计算累加和

编写程序,计算 1+2+3+…+n 和并显示结果。

#include <iostream>
using namespace std;
int main()
{
	int sum=0,i,n;
	cin>>n;
	for(i=1; i<=n; i++) {
		sum=sum+i;
	}
	cout << sum<<endl;
	return 0;
}

# 例子:乘法表

编写程序,按正三角形式显示九九乘法表。
格式如下:

1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
……
1*9=9 2*9=18 3*9=27……9*9=81
#include <iostream>
using namespace std;
int main()
{
	int i,j;
	for(i=1; i<=9; i++) // 外层循环
	{ 
		for(j=1; j<=i; j++) // 内层循环
		{
			cout << j<<"*"<<i<<"="<<j*i<<"\t"; // \t 输出按列对齐
		}
		cout<<endl;
	}
	return 0;
}

# 已知条件循环

while (表达式)
{
	<语句序列>
}
do
{
	<语句序列>
} while (表达式);
graph LR
    subgraph do-while
    c(语句序列) --> d{条件?} --> |成立| c
    d --> |不成立| e[ ]
    end
	subgraph while
	g --> f
	f{条件?} --> |成立| g[语句序列]
	f --> |不成立| n[ ]
	end

# 例子:求 e 值

编写程序,使用下列级数近似计算 e 值,直到最后一个通项 < 10-7 为止。

e=1+11!+12!+...+1n!+...e = 1 + \frac{1}{1!} + \frac{1}{2!} + ... + \frac{1}{n!} + ...

ui=1i!=1(i1)!/i=ui1/iu_i = \frac{1}{i!} = \frac{1}{(i - 1)!}/i = u_{i-1} / i

while
#include <iostream>
using namespace std;
int main()
{
	double e = 1.0,u = 1.0; // 基数 e,通项 u
	int n = 1;
	while(u >= 1.0E-7)
	{
		u = u/n; // 通项
		e = e+u; // 累加和
		n = n+1;
	}
	cout << "e = " << e << " ( n = " << n << " )" << endl;
	return 0;
}
do-while
#include <iostream>
using namespace std;
int main()
{
	double e=1.0,u = 1.0;
	int n= 1;
	do
	{ 
		u = u/n;
		e = e+u;
		n = n+1;
	} while (u>=1.0E-7);
	cout << "e = " << e << " ( n = " << n << " )" << endl;
	return 0;
}

# 例子:计算实数 n 次方根

编写程序,能够根据输入的实数 x 和 n,计算 x 的 n 次方根。
具体要求:

  • 输入 0 0 时,程序结束
  • 当 (x<0 且 n<=0) 或 (x<=0 且 1/n 不为整数) 时,显示 “输入错误” 并允许用户继续输入
  • 否则计算并显示 x 的 n 次方根并允许用户继续输入
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	double x,n;
	while (1)
	{
		cin>>x>>n;
		if(x==0&&n==0)
		{
			cout<<"Program terminated"<<endl;
			break; // 能够跳出所在位置最近的一层循环
		}
		else
		if((x<0&&n<=0)||(x<0&&1/n!=int(1/n)))
		{
			cout<<"error reinput"<<endl;
			continue; // 能够跳过后续语句,开始新一轮的循环
		}
		cout<<x<<"\t"<<n<<"th root"<<pow(x,1.0/n)<<endl;
	}
	return 0; 
}

# 实例

# 解一元二次方程

输入一元二次方程的 a,b,c 三个系数,解一元二次方程

ax2+bx+c=0ax^2+bx+c=0

输出两个根(含复根)。

# 问题分析

x=b±b24ac2ax = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}

Δ>0? Δ<0?

# 算法描述

输入a,b,c;
如果a=0,
	如果b=0,
		输出“输入的系数不构成方程”;
	否则(即b≠0)
		计算单根x=-c/b
		输出单根x
否则(即a≠0)
	计算 delta=b*b-4*a*c
	如果 delta>0
		delta=sqrt(delta)
		输出x1=(-b+delta)/2a和x2=(-b-delta)/2a
	否则
		delta=sqrt(-delta)
		输出f复根:
			x1=-b/2a+j*delta/2a;
			x2=-b/2a-j*delta/2a(注意j是虚数单位)
结束

# 源程序

#include <iostream> // 包含需要的头文件
#include <cmath> // 求根函数 sqrt 需要的头文件
using namespace std; // 名字空间
int main() // 主函数
{
	double a,b,c; // 定义变量保存系数
	double delta; // 表示根的判别式
	double x,x1,x2; // 表示根
	cout<<"请输入一元二次方程的三个系数a,b,c:";
	cin>>a>>b>>c; // 输入一元二次方程的系数
	if(a==0) { // 二次项系数等于 0
		if(b==0) // 一次项系数也等于 0,不是方程
		{
			cout<<"输入的系数不构成方程"<<endl;
		}
		else // 二次项系数等于 0,一次项系数不为 0
		{ 
			x=-c/b; // 计算单根
			cout<<"实际为一元一次方程,根为"<<x<<endl;
		}
	}
	else // 二次项系数不为 0
	{
		delta=b*b-4.0*a*c; // 计算判别式的值
		if(delta>=0) // 判别式大于等于 0,有实根
		{ 
			delta=sqrt(delta); // 判别式开方
			x1=(-b+delta)/2.0/a; // 根 1
			x2=(-b-delta)/2.0/a; // 根 2
			cout<<"方程有实根,它们是:"<<endl;
			cout<<"x1="<<x1<<", x2="<<x2<<endl;
		}
		else // 判别式小于 0,有复根
		{
			delta=sqrt(-delta); // 判别式变号开方
			x1=-b/2.0/a; // 实部
			x2=delta/2.0/a; // 虚部
			cout<<"方程有复根,它们是:"<<endl;
			cout<<"x1="<<x1<<"+j"<<x2<<", x2="<<x1<<"-j"<<x2<<endl;
			// 由于 C++ 中没有复数类型,所以程序中先计算复根的实部和虚部,在输出时构造复数的形式。
		}
	}
	return 0;
}

# 一个简单的计算器

编程一个简单的计算器功能,实现简单的加、减、乘、除表达式的计算。
设用户输入的表达式具有如下格式:
<操作数1> <运算符> <操作数2>

# 问题分析

用户输入表达式后,程序要判断是什么运算,然后再做相应的处理。
该问题可以使用 if,或 if...else 解决。
对于分支较多的情况,C++ 提供 switch 语句。switch 语句也叫开关语句、多分支语句,它计算一个表达式的值,根据结果的不同,执行不同的分支处理语句。

# 算法描述

用num1,num2,op分别表示输入的表达式的两个操作数和一个运算符。
如果op='+',则result=num1+num2,输出result;
如果op='-',则result=num1-num2,输出result;
如果op='*',则result=num1*num2,输出result;
如果op='/',则
	如果num2=0,显示“除数为0”
	否则,计算result=num1/num2,输出result;
其他,显示“运算符错误”。

# 源程序

#include<iostream> // 包含输入输出头文件
#include<cmath>
using namespace std; // 指定名字空间
int main() // 主函数
{
	double num1,num2;
	char op; // 声明字符变量,存放操作符
	double result; // 声明变量,存放计算机结果
	char caption1[20]="Error,Divided by 0!";
	char caption2[20]="Invalid opereator!";
	cout<<"Please input the expression:";
	cin>>num1>>op>>num2;
	switch(op) 
	{
		case '+':
			result=num1+num2;
			cout<<num1<<op<<num2<<"="<<result<<endl;
		break; // 中断
		case '-':
			result=num1-num2;
			cout<<num1<<op<<num2<<"="<<result<<endl;
		break; // 中断
		case '*': // 是乘号
			result=num1*num2; // 计算积
			cout<<num1<<op<<num2<<"="<<result<<endl;
		break;
		case '/': // 是除号
			if(fabs(num2)<1.0e-8) // 除数为 0
			{
				cout<<caption1<<endl;
			}
			else // 除数不为 0
			{
				result=num1/num2; // 计算商
				cout<<num1<<op<<num2<<"="<<result<<endl;
			}
		break;
		default : // 以上情况都不是
			cout<<caption2<<endl;
	}
	return 0;
}

switch 的条件是整型表达式;
fabs() 是求绝对值的函数,它包含在 cmath 头文件中;
在每一个 case 处理的最后都有一个 break 语句。

# 寻找自幂数

用户输入位数 n,找出并显示出所有 n 位的自幂数。
数学家发现了很多有趣的数字。
比如,153,一个普通的三位数,然而 13+53+3^3=153, 即它的各位数字的三次方的和等于这个数本身。
更一般地,一个 n 位正整数,哪些数的各位数字的 n 次方的和加起来 还等于这个数呢? 数学家称这样的数为自幂数,也叫自恋数。

n 为 1 时,自幂数称为独身,0,1,2,3,4,5,6,7,8,9 都是自幂数。
n 为 2 时,没有自幂数。
n 为 3 时,自幂数称为水仙花数,153 就是一个水仙花数。
n=4,称为四叶玫瑰数。
n=5,称为五角星数。
n=6,称为六合数。
n=7,称为北斗七星数。
n=8,称为八仙数。
n=9,称为九九重阳数。

# 问题分析

n 位自幂数,各位数字的 n 次方的和加起来还等于这个数。

  1. 找出 “各位”
    如 153,找个位,可用 153%10=3;
    十位,(153/10)%10=5,依次类推。

    直接求余,就是最低位的数字,除 10,原来的十位就成为新的最低位,重复这一过程,就可以求出各位,直到这个数成为 0。

  2. n 次方的计算:使用数学函数 pow(x,n)

  3. 构造 n 位数。
    0 是最小的一位数,10 的 1 次方是最小的两位数,10 的平方是最小的三位数,那么,10 的 n-1 次方就是最小的 n 位数。

# 算法描述

  1. 输入位数 n。
  2. 计算 n 位数的起始值和终止值
    start=10n-1
    end=10n-1
    i = start
  3. 如果 i>end,转 9 结束
  4. m = i, sum = 0
  5. 如果 m = 0,转 7
  6. d = m%10,sum=sum+dn,m=m/10,转 5
  7. 如果 sum=i,显示 i
  8. i=i+1,转 3
  9. 结束

# 源程序

#include <iostream> // 包含需要的头文件
#include<cmath> // 数学函数需要的头文件
using namespace std; // 名字空间
int main() {
	int n; // 表示数的位数
	int start,end; // 表示 n 位数的起始值和终止值
	int m; // 待分解各位的数,即待判断的数
	int digit; // 某个数位的值
	int sum; // 各位数的 n 次方的和
	int i; // 循环变量,待检验的数
	cout<<"求n位自幂数,请输入位数:"; // 提示信息
	cin>>n; // 输入位数
	while(n>0) // 大于 0 时计算
	{
		start=pow(10,n-1); //n 位数的起始值
		end=pow(10,n)-1; //n 位数的终止值
		cout<<n<<"位自幂数:"; // 输出说明信息
		
		for(i=start;i<=end;i++) // 从起始值到终止值逐个检验
		{
			m=i; // 将 i 赋给 m
			// 检验过程中 m 的值会改变,而 i 的值不变
			sum=0; // 各位数的 n 次方和,检验前赋 0
			while(m!=0) //m 开始为待检验的数
			{
				digit=m%10; // 取最低位数字
				sum=sum+pow(digit,n); //n 次方,再求和
				m=m/10; // 去掉个位,刚才的十位成为新个位
			}
			// 上面的循环结束时 sum 就是各位数字的 n 次方的和
			if(sum==i){ // 逻辑表达式的值为 true 时,表示是自幂数
				cout<<i<<" "; // 显示该数
			}
		}
		cout<<endl; // 换行
		cout<<“求n位自幂数,请输入位数:;
		cin>>n; // 再输入一个 n 表示位数
	} //while 循环
	cout<<endl;
	return 0;
}

# 思路扩展

  • 如果一种计算会破坏(或改变)某个变量的值,而这个原始值在后面的计算中还会使用,那就先将其赋值给另一个变量,使用新变量作 “破坏性” 计算,随时可以通过原来的变量获得原始值。

这实际是计算机科学中常用的一种 “冗余” 的思想,要获得某种保障,有意使用更多的时间、空间。

  • 分离各位数字的相反运算是合成一个数,例如有三个变量,a,b,c,分别存放一位整数,比如 1,2,3,如果将它们合成为 a 作百位,b 作十位,c 作各位的三位数呢?

  • C++ 中,int 型变量能表示的最大正整数为 2147483647,它不过 10 位,那么有 11,12,13 位的自幂数吗?如果有,怎样计算呢?自幂数是有限的吗?如果有,有多少呢?

# 课堂讨论

# 单路和双路分支使用

  1. 在什么情况下,应该使用分支嵌套?
  1. 存在两个或者两个以上的逻辑判断
  2. 逻辑判断是包含关系而不是排列关系。比如 a>0 ,a==0,a<0 就是三个逻辑并列关系,互不补充。而 a>0 和 a>100 是包含关系。当 a>0 的情况实现时,我们再去判断 a>100 的情况。
  3. 可以理解为主条件里面的附带条件,如果主条件不成立,附带的条件也不会成立。
  1. 还有什么算法能够实现在三个数中,寻找最大数?
三目运算符
a>=b?(max=a):(max=b);
max>=c?(max=max):(max=c);
cout<<max<<endl;

# 多路分支使用

  1. 在什么情况下,应该使用多路分支语句?

需要多个条件判断时应该使用。最典型的例子就是求 GPA。
此外分段函数也可以使用,比如交电费。0-50 度三毛钱,50-100 的部分六毛钱。100-200 部分九毛钱,超过 200,一块四。这种问题。

  1. 如果想将百分制成绩划分更多等级,如 60-64 为 2,65-69 为 2.5, 70-74 为 3,75-79 为 3.5 ……,该如何构造 switch 语句中的表达式?

百分制更细划分:除以 5。

#include<iostream>
using namespace std;
int main()
{
	int avg;
	float gpa;
	cin>>avg;
	switch(avg/5)
	{
		case 20:
		case 19:gpa=5;break;
		case 18:gpa=4.5;break;
		case 17:gpa=4;break;
		case 16:gpa=3.5;break;
		default:gpa=1;
	}
	cout<<gpa;
	return 0;
}

# 已知条件循环使用

  1. 在求 e 值的程序中,能否将通项 u 的类型由双精度改为整型,初值由 1.0 改为 1?为什么?

不能,如果将初值由 1.0 改为 1,那么其结果会为整数而不会保留小数部分,会损失精度。

  1. 请总结 break 语句在多路分支和循环语句中的作用。

break 语句通常用在循环语句和开关语句中。

  1. 当 break 用于开关语句 switch 中时,可使程序跳出 switch 而执行 switch 以后的语句;
  2. 当 break 语句用于 do-while、for、while 循环语句中时,可使程序终止循环。而执行循环后面的语句,通常 break 语句总是与 if 语句联在一起。即满足条件时便跳 出循环。
  1. 在什么情况下,可以使用死循环?

当循环条件永远为真时,但使用 break 关键字可以强行跳出循环体。

# 随堂练习

  1. 结构化程序由三种基本结构组成,不包括

    • 顺序结构
    • 选择结构
    • 控制结构
    • 循环结构
  2. 下列语句中错误的是

    • if (a>b) cout<<a;
    • if (&&) a=m;
    • if (1) a=m; else a=n;
    • if (a>0); else a=n;
  3. 与分支语句有关的关键词有 if、else、switch、case 和 break。

  4. 若有定义 float w;int a,b; ,则合法的 switch 语句是

    • switch(w)
      {
          case 1.0: cout<<"1.0";
          case 2.0: cout<<"2.0";
      }
    • switch(a)
      {
          case 1 cout<<"1";
          case 2 cout<<"2";
      }
    • switch(b)
      {
          case 1: cout<<"1";
          default: cout<<"default";
          case 1+2: cout<<"3";
      }
    • switch(a+b)
      {
          case 3: cout<<"3";
          case 1+2: cout<<"1+2";
          default: cout<<"default";
      }
  5. for 循环语句是先执行循环体内的语句序列,后判断条件表达式。

  6. 下列程序段循环了几次

    int x=-9;
    while(++x){}
    • 8
    • 9
    • 10
    • 无限

    ++x 到 0 时,即 false,跳出循环。
    先 +1,再判断 x 的值,所以 while 的条件是从 x=8 开始的。
    D

# 单元测试

  1. 假定所有变量均已正确说明,下列程序段运行后,x 的值是

    a=b=c=0;
    x=35;
    if (!a)  x--;
    else  if (b);
    if (c)  x=3;
    else  x=4;
    • 35
    • 4
    • 3
    • 34
  2. C++ 语言中 while 循环和 do...while 循环的主要区别是

    • while 的循环控制条件比 do...while 的循环控制条件严格
    • do...while 的循环体至少无条件执行一次
    • do...while 允许从外部转到循环体内
    • do...while 的循环体不能是复合语句
  3. while 后面的 “条件表达式” 一定要用一对 括起来。

    • 双引号 " "
    • 花括号 { }
    • 方括号 [ ]
    • 圆括号 ( )
  4. 执行语句序列:

    int x=3;
    do
    {
      x-=2;
      cout<<x;
    }while(!(--x));

    输出结果是

    • 1 -2
    • 死循环
    • 1
    • 3 0

    B

  5. 在 C++ 语言中,所谓选择结构,就是按照 有选择地执行程序中的语句。

    • 给定符号
    • 给定程序
    • 给定条件
    • 给定数值
  6. 在 C++ 语言中,表示一条语句结束的标号是

    • //
    • }
    • ;
    • #
  7. 下列程序段的输出是

    int a=2, b=-1, c=2;
    if(a<b)
        if(b<c) c=0;
    else
        c+=1;
    cout<<c<<endl;
    • 1
    • 0
    • 2
    • 3

    D,注意 else 对应的不是第一个,而是第二个,不要被缩进误导了

  8. 当在程序中执行到 continue 语句时,将结束所在循环语句中循环体的一次执行。

  9. 当在程序中执行到 break 语句时,将结束本层循环类语句或 switch 语句的执行。

  10. 用 {} 括起来的语句叫复合语句。

阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Ruri Shimotsuki 微信支付

微信支付

Ruri Shimotsuki 支付宝

支付宝

Ruri Shimotsuki 贝宝

贝宝