实验五 结构体和指针
1 实验目的
(1)学习如何使用结构体的指针作为函数的参数,通过传地址的方式,在
被调用函数中修改主调函数中的多个结构体变量的方法。
(2)学习如何把逻辑结构相同的部分抽象为函数,以提高代码的可重用性,
达到提高程序的可维护性的目的。
2 实验内容
2.1 模拟画图功能
(1)问题描述
模拟计算机的画图功能,能够模拟画圆和长方形的功能。程序主要功能如下:
① 提供一个如下的主菜单。
- Circle (圆)
- Rectangle (长方形)
- Exit (退出)
② 不断接受用户的选择(整数),直至用户选择 0 为止。如果用户输入了系
统尚未支持的选择(比如 3),则给出相应的提示信息,并继续选择。
③ 如果用户选择了圆或长方形,则进一步提示用户输入两个点,分别称为
起点和终点,如下图所示。坐标仅考虑正整数的情况。要求终点的坐标
大于起点的坐标,否则给出相关的提示信息并返回主菜单。
④ 模拟画出圆和长方形。画圆时,要计算 startPoint 和 endPoint 所构成的正
方形的内切圆的圆心和半径。若 startPoint 和 endPoint 所构成的不是正方
形,则给出相关的提示信息并返回主菜单。
(2)问题要求
① 定义一个 Point 结构体,用来表示显示屏上的点;
② 实现以下函数(图 2),使得主程序(图 3)输出图 4 中的结果。
主程序如图 3 所示。
运行结果如图 4 所示。
注意,上述结果未能列出所有可能的执行情况。请认真思考,相关的提示信
息应该分别在由哪些函数中输出?
源代码
#include <iostream>
using namespace std;
struct Point {
int x; // 横坐标
int y; // 纵坐标
};
void printPoint(const struct Point * p) {
cout << "(" << p->x << ", " << p->y << ")";
}
// 得到用户输入的两个点
void getTwoPoints(struct Point *startP, struct Point *endP) {
cout << "Please input the coordinate (x, y) of the start point: ";
cin >> (startP->x);
cin >> (startP->y);
cout << "Please input the coordinate (x, y) of the end point: ";
cin >> endP->x;
cin >> endP->y;
}
// 显示主菜单
void displayMenu() {
cout << "*******************************\n";
cout << " 1. Circle (圆)\n";
cout << " 2. Rectangle (长方形)\n";
cout << " 0. Exit (退出)\n";
cout << "*******************************\n";
cout << "Please select the shape: ";
}
// 根据用户给出的起点和终点,画圆
bool drawCircleAd(struct Point *startP, struct Point *endP) {
struct Point center; // 圆心坐标
int radius; // 半径
if ((endP->x - startP->x) != (endP->y - startP->y)) {
cout << "Not a circle, please input two points again!\n";
return false;
} else {
center.x = (startP->x + endP->x) / 2; // 计算圆心横坐标
center.y = (startP->y + endP->y) / 2; // 计算圆心纵坐标
radius = (endP->x - startP->x) / 2; // 计算半径
// 模拟画圆
cout << "Draw a circle at center ";
printPoint(¢er);
cout << " with radius " << radius << endl;
return true;
}
}
// 根据用户给出的起点和终点,画长方形
bool drawRectangleAd(struct Point *startP, struct Point *endP) {
int width, height;
width = endP->x - startP->x; // 长
height = endP->y - startP->y; // 宽
if (width <= 0 || height <= 0) {
cout << "Not a rectangle, please input two points again!\n";
return false;
}
// 模拟画长方形
cout << "Draw a rectangle at topleft ";
printPoint(startP);
cout << ", whose width is " << width << " and height is " << height << endl;
return true;
}
int main() {
int choice = 1; // 用户选择
struct Point startP; // 起点
struct Point endP; // 终点
bool flag = false;
while (choice) {
displayMenu();
cin >> choice;
flag = false;
while (!flag) {
switch (choice) {
case 1:
getTwoPoints(&startP, &endP);
flag = drawCircleAd(&startP, &endP);
break;
case 2:
getTwoPoints(&startP, &endP);
flag = drawRectangleAd(&startP, &endP);
break;
case 0:
cout << "Good Bye!\n";
flag = true;
break;
default:
cout << "Error Input! Please select again!\n";
flag = true;
break;
}
}
}
return 0;
}
// 根据用户给出的起点和终点,画圆
void drawCircle(struct Point *startP, struct Point *endP) {
struct Point center; // 圆心坐标
int radius; // 半径
if ((endP->x - startP->x) != (endP->y - startP->y))
cout << "Not a circle, Select again\n";
else {
center.x = (startP->x + endP->x) >> 1; // 计算圆心横坐标
center.y = (startP->y + endP->y) >> 1; // 计算圆心纵坐标
radius = (endP->x - startP->x) >> 1; // 计算半径
// 模拟画圆
cout << "Draw a circle at center ";
printPoint(¢er);
cout << " with radius " << radius << endl;
}
}
// 根据用户给出的起点和终点,画长方形
void drawRectangle(struct Point *startP, struct Point *endP) {
int width, height;
width = endP->x - startP->x; // 长
height = endP->y - startP->y; // 宽
// 模拟画长方形
cout << "Draw a rectangle at topleft ";
printPoint(startP);
cout << ", whose width is " << width << " and height is " << height << endl;
}
void ShapesMain() {
int choice = 1; // 用户选择
struct Point startP; // 起点
struct Point endP; // 终点
while (choice) {
displayMenu();
cin >> choice;
switch (choice) {
case 1:
getTwoPoints(&startP, &endP);
drawCircle(&startP, &endP);
break;
case 2:
getTwoPoints(&startP, &endP);
drawRectangle(&startP, &endP);
break;
case 0:
cout << "Good Bye!\n";
break;
default:
cout << "Error Input! Please select again!\n";
break;
}
}
}
2.2 计算所得税
(1)问题描述
一个国家计算所得税的规则由如下的不确定多条规则构成:
(2)问题要求
① 请定义一个结构体,用来存放一条规则。
② 根据用户输入的规则条数,使用结构体的数组存放所有的规则。
③ 定义一个子函数 inputRules(struct Rule rules[], int n),根据用户输入的规则
条数,从键盘得到所有规则,存放至数组。
④ 定义一个子函数计算所得税。
// 参数 income 表示个人收入
double computeTax(struct Rule rules[], int n, int income);
⑤ 在主函数中,一次录入规则后,可以循环计算任意多个人的所得税,直
到输入的收入为-1 为止。
⑥ 请至少测试以下情况
- 输入规则后,在录入收入时,直接录入-1;
- 收入为 0;
- 收入小于 ;
- 收入等于 的各种情况;
- 收入大于 ;
- 收入在 和 之间的情况。
(3)程序运行要求
程序的一次运行结果如下:
①
请输入规则的条数:3
请输入第 1 条规则:800 3
请输入第 2 条规则:2000 4
请输入第 3 条规则:5000 3
②
纳税规则如下:
纳税线 税率
800 3
2000 4
5000 3
③
请输入您的收入:0
您的收入是:0,应缴所得税:0.00 元。
④
请输入您的收入:800
您的收入是:800,应缴所得税:0.00 元。
⑤
请输入您的收入:801
您的收入是:801,应缴所得税:0.03 元。
⑥
请输入您的收入:2000
您的收入是:2000,应缴所得税:36.00 元。(2000 – 800)* 3/100
⑦
请输入您的收入:1999
您的收入是:1999,应缴所得税:35.97 元。
⑧
请输入您的收入:5000
您的收入是:5000,应缴所得税:156.00 元。[(2000 – 800)*3+(5000 –
2000)*4]/100
请输入您的收入:10000
您的收入是:10000,应缴所得税:306.00 元。[(2000 – 800)*3+(5000 –
2000)*4+(10000 – 5000)*3] / 100
⑨
请输入您的收入:-1
再见
请按任意键继续…
源代码
#include <iostream>
#include <iomanip>
using namespace std;
struct TaxRule {
int baseLine; // 纳税线
int rate; // 税率
};
// 静态全局变量,指向征税规则的指针,仅在本cpp文件内全局有效
static struct TaxRule* taxRules;
// 静态全局变量,征税规则的条数,仅在本cpp文件内全局有效
static int ruleNum = 0;
void setRules(); // 从用户输入获得征税规则
void displayRules(); // 显示征税规则
double compTax(int); // 计算给定收入的所得税
// 从键盘输入纳税规则
void setRules() {
// 获得规则的条数
cout << "请输入规则的条数:";
cin >> ruleNum;
// 生成指定条数的规则
taxRules = new struct TaxRule[ruleNum];
// 获得用户输入的规则
for (int i = 0; i < ruleNum; i++) {
cout << "请输入第 " << (i + 1) << " 条规则:";
cin >> taxRules[i].baseLine >> taxRules[i].rate;
}
}
// 显示规则
void displayRules() {
cout << "\n纳税规则如下:\n";
cout << "纳税线\t税率\n";
for (int i = 0; i < ruleNum; i++) {
cout << taxRules[i].baseLine << "\t\t" << taxRules[i].rate << endl;
}
}
// 计算纳税金额
double compTax(int income) {
double sum = 0.0;
int i = 0;
if (taxRules[0].baseLine > income)
return 0.0;
// 逐次检查每一个纳税线,直到检查完说有纳税线或者找到一个比输入大的纳税线。
// 在此过程中,累加两个相邻纳税线之间的税额。
for (i = 1; i < ruleNum && taxRules[i].baseLine <= income; i++) {
//累加计算纳税金额。为了提高速度,税率先不除以100.
sum = sum + (taxRules[i].baseLine - taxRules[i - 1].baseLine) * taxRules[i - 1].rate;
}
// for循环结束时,要么找到了某个大于income的纳税线,
// 要么所有纳税线都比income小。
// 不管是哪种情况,都看income超过前一个纳税线多少。
// 如果所有纳税线都比income小,此时i和ruleNum相等,
// 我们就可以把最后一个纳税线看作前一个纳税线。
int remains = income - taxRules[i - 1].baseLine;
// 将超出部分按前一个纳税线计算纳税金额
sum = sum + remains * taxRules[i - 1].rate;
// 最后计算所得的税额除以100
return sum / 100;
}
/**
* 计算所得税的主类。
* 与TaxRule类之间是依赖关系。因为其main方法通过局部变量使用了TaxRule的实例。
* @author Jianliang Xu
*
*/
int main() {
setRules();
// 显示规则
displayRules();
// 存放用户输入的收入金额的局部变量
int income = 0;
// 接受用户的输入并计算所得税
while (true) {
cout << "\n请输入您的收入:";
cin >> income;
if (income == -1) {
cout << "Good Bye!!" << endl;
break;
}
cout << "您的收入是:" << income << "," << "应缴所得税:" << setprecision(2) << fixed << compTax(income) << "元。" << endl;
}
return 0;
}