第五章 选择控制结构
分治策略:任务分解细化
程序设计语言:为了让计算机执行由高级语言编写的程序指令,必须把这些指令从高级语言形式转换成计算机能理解的机器语言形式,这种转换是由编译器来完成的
算法:为解决一个具体问题而采取的确定、有限、有序、可执行的操作步骤
数据结构+算法=程序(这个公式仅对面向过程的语言成立)
数据结构是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合
算法是对操作或行为的描述,算法代表着用系统的方法描述解决问题的策略
算法的描述方式:
- 自然语言描述(通俗易懂;冗长,不易直接转化为程序)
- 流程图描述(形象直观、一目了然,易于理解和发现程序中存在的错误;所占篇幅较大)
- NS结构化流程图描述
- 伪码描述
关系运算符
- <
- >
- <=
- >=
- \==
- !=
注意:==是比较,=是赋值,将“==”误写为“=”在C语言语法上是允许的,不会提示错误,但将导致错误的运行结果
条件表达式:用非0值表示“真”,用0值表示“假”
//L5-1(单分支控制)
#include <stdio.h>
main()
{
int a, b, max;
printf("Input a, b:");
scanf("%d,%d", &a, &b);
if (a > b) max = a;
if (a <= b) max = b;
printf("max = %d\n", max);
}
```c
//运行结果
Input a, b:3,5
max = 5
//L5-2(双分支控制)
#include <stdio.h>
main()
{
int a, b, max;
printf("Input a, b:");
scanf("%d,%d", &a, &b);
if (a > b) max = a;
else max = b; /* 相当于a<=b的情况 */
printf("max = %d\n", max);
}
//L5-3(条件运算符)
#include <stdio.h>
main()
{
int a, b, max;
printf("Input a, b:");
scanf("%d,%d", &a, &b);
max = a > b ? a : b;/* 用条件表达式计算两整数的最大值*/
printf("max = %d\n", max);
}
用一对花括号将一组逻辑相关的语句括起来,称为复合语句(类似于Verilog中的begin end)
//L5-4
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define EPS 1e-6
main()
{
float a, b, c, disc, p, q;
printf("Please enter the coefficients a,b,c:");
scanf("%f,%f,%f", &a, &b, &c);
if (fabs(a) <= EPS) /* a=0时,输出"不是二次方程" */
//浮点数并非真正意义上的实数,只是其在某种范围内的近似,因此也就只能用近似的方法将实数与0进行比较
{
printf("It is not a quadratic equation!\n");
exit(0);//exit是C语言提供的标准库函数,作用是终止整个程序的执行,强制返回操作系统
//调用函数exit()需要在程序开头包含头文件<stdlib.h>
}
disc = b * b - 4 * a * c; /* 计算判别式 */
p = - b / (2 * a);
q = sqrt(fabs(disc)) / (2 * a);
if (fabs(disc) <= EPS) /* 判别式等于0时,输出两相等实根 */
{
printf("x1 = x2 = %.2f\n", p);
}
else
{
if (disc > EPS) /* 判别式大于0时,输出两不等实根 */
{
printf("x1 = %.2f, x2 = %.2f\n", p+q, p-q);
}
else /* 判别式小于0时,输出两共轭复根 */
{
printf("x1 = %.2f+%.2fi, ", p, q);
printf("x2 = %.2f-%.2fi\n", p, q);
}
}
}
用于多路选择的switch语句(类似于Verilog中的case语句)
//L5-5
#include <stdio.h>
main()
{
int data1, data2;
char op;
printf("Please enter an expression:");
scanf("%d%c%d", &data1, &op, &data2); /* 输入运算表达式 */
switch (op) /* 根据输入的运算符确定执行的运算 */
{
case '+': /* 加法运算 */
printf("%d + %d = %d \n", data1, data2, data1 + data2);
break;//只有switch语句和break语句配合使用,才能形成真正意义上的多分支
case '-': /* 减法运算 */
printf("%d - %d = %d \n", data1, data2, data1 - data2);
break;
case '*': /* 乘法运算 */
printf("%d * %d = %d \n", data1, data2, data1 * data2);
break;
case '/': /* 除法运算 */
if (0 == data2) /* 为避免除0错误,检验除数是否为0 */
printf("Division by zero!\n");
else
printf("%d / %d = %d \n", data1, data2, data1 / data2);
break;
default: /* 处理非法运算符 */
printf("Invalid operator! \n");
}
}
每个case后常量的次序发生改变时,不影响程序的运行结果,但不能有重复的case出现
从执行效率角度考虑,一般将发生频率高的情况放在前面
//L5-6
#include <math.h>
#include <stdio.h>
main()
{
float data1, data2;
char op;
printf("Please enter the expression:\n");
scanf("%f %c%f", &data1, &op, &data2); /* %c前有一个空格 */
switch (op)
{
case '+':
printf("%f + %f = %f \n", data1, data2, data1 + data2);
break;
case '-':
printf("%f - %f = %f \n", data1, data2, data1 - data2);
break;
case '*':
case 'x':
case 'X':
printf("%f * %f = %f \n", data1, data2, data1 * data2);
break;
case '/':
if (fabs(data2) <= 1e-7) /* 实数与0比较 */
printf("Division by zero!\n");
else
printf("%f / %f = %f \n", data1, data2, data1 / data2);
break;
default:
printf("Invalid operator! \n");
}
}
逻辑运算符
- !:逻辑非
- &&:逻辑与
- ||:逻辑或
运算符&&和||都具有“短路”特性
程序测试:
- 程序测试是确保程序质量的一种有效手段
- 穷尽测试、抽样检查
- 程序测试只能证明程序有错,而不能证明程序无错
- 程序测试的目的是为了尽可能多的发现程序中的错误
测试用例:
- 白盒测试(结构测试):测试程序的内部结构已知,按程序的内部逻辑设计测试用例
- 黑盒测试(功能测试):不考虑程序内部的逻辑结构和处理过程,只检查程序的功能是否符合它的功能说明
- 结合使用:选择有限数量的重要路径进行白盒测试,对重要的功能需求进行黑盒测试
//L5-7
#include <stdio.h>
#include <math.h>
#define EPS 1e-1
main()
{
float a, b, c;
int flag = 1;
printf("Input a,b,c:");
scanf("%f,%f,%f", &a, &b, &c);
if (a+b>c && b+c>a && a+c>b)
{
if (fabs(a-b)<=EPS && fabs(b-c)<=EPS && fabs(c-a)<=EPS)
{
printf("等边"); /* 等边 */
flag = 0; /* 置标志变量flag为0值 */
}
else if (fabs(a-b)<=EPS || fabs(b-c)<=EPS|| fabs(c-a)<=EPS)
{
printf("等腰"); /* 等腰 */
flag = 0; /* 置标志变量flag为0值 */
}
if (fabs(a*a+b*b-c*c)<=EPS || fabs(a*a+c*c-b*b)<=EPS
|| fabs(c*c+b*b-a*a)<=EPS)
{
printf("直角");
flag = 0;
}
if (flag)
{
printf("一般");
}
printf("三角形\n");
}
else
{
printf("不是三角形\n");
}
}
```c
//测试结果
Input a,b,c:3,4,5
直角三角形
Input a,b,c:4,4,5
等腰三角形
Input a,b,c:,3,4,6
不是三角形
Input a,b,c:3,4,9
不是三角形
Input a,b,c:10,10,14.14
等腰直角三角形
Input a,b,c:4,4,4
等边三角形
边界测试:
在选用测试用例时,不仅要选用合理的输入数据,还应选用不合理的以及某些特殊的输入数据或者临界的点,对程序进行测试,称为边界测试
//L5-8
#include <stdio.h>
main()
{
int score, mark;
printf("Please enter score:");
scanf("%d", &score);
if (score<0 || score>100)
{
mark = -1;
}
else
{
mark = score / 10;
}
switch (mark)
{
case 10:
case 9:
printf("%d--A\n", score);
break;
case 8:
printf("%d--B\n", score);
break;
case 7:
printf("%d--C\n", score);
break;
case 6:
printf("%d--D\n", score);
break;
case 5:
case 4:
case 3:
case 2:
case 1:
case 0:
printf("%d--E\n", score);
break;
default:
printf("Input error!\n");
}
}
//5-9
#include <stdio.h>
main()
{
int a, b, max;
printf("Input a, b:");
scanf("%d,%d", &a, &b);
max = a > b ? a : b;
printf("max = %d\n", max);
}
//5-10
#include <stdio.h>
main()
{
int a, b, max, ret;
printf("Input a, b:");
ret = scanf("%d,%d",&a, &b); /* 记录scanf函数的返回值 */
if (ret != 2) /* 根据scanf函数返回值,判断输入数据个数或者格式是否错误 */
{
printf("Input data quantity or format error!\n");
while(getchar() != '\n'); /* 清除输入缓冲区中的错误数据 */
}
else /* 此处可以是正确读入数据后应该执行的操作 */
{
max = a > b ? a : b;
printf("max = %d\n", max);
}
}
虽然函数scanf()不进行参数类型匹配检查,但是通过检验scanf()的返回值是否为应读入的数据项数,可以判断输入的数据项数和格式(包括输入格式错误、存在非法字符、无数据可读等)是否正确
位运算符:
位运算就是对字节或字内的二进制数位进行测试、抽取、设置或移位等操作
其操作对象不能是float、double、long double等其他数据类型,只能是char和int类型
~:按位取反
«,»:左移位、右移位(无论是左移位还是右移位,从一端移走的位不移入另一端,移出的位的信息都丢失了)
算术移位
逻辑移位
&:按位与,可用于对字节中的某位清零
^:按位异或,可用于对字节中的某位置1
|:按位或
注意:按位取反是用补码进行计算的。
如12为0000000000001100,取反得到补码1111111111110011,反码为1111111111110010,原码为1000000000001101,即为-13
//L5-11
#include <stdio.h>
main()
{
int x=12, y=8;
printf("\n%5d%5d%5d", !x, x||y, x&&y);
printf("\n%5u%5d%5d", ~x, x|y, x&y);
printf("\n%5d%5d%5d\n", ~x, x|y, x&y);
}
```c
//运行结果
0 1 1
4294967283 12 8
-13 12 8