From fca46062d773a23f5e911c119bd0bbf1deeea786 Mon Sep 17 00:00:00 2001 From: gameloader Date: Wed, 12 Oct 2022 12:38:35 +0800 Subject: [PATCH] add bju homework --- content/posts/bj_homework.md | 442 +++++++++++++++++++++++++++++++++++ 1 file changed, 442 insertions(+) create mode 100644 content/posts/bj_homework.md diff --git a/content/posts/bj_homework.md b/content/posts/bj_homework.md new file mode 100644 index 0000000..2325dcb --- /dev/null +++ b/content/posts/bj_homework.md @@ -0,0 +1,442 @@ ++++ +title = "Bj Homework" +date = 2022-10-12 +draft = false +author = "Logic" ++++ + +## 第一部分 {#第一部分} + +1. str大小为指针的大小即8字节,p的大小也为指针的大小即8字节。因为C++传数组给一个函数,数组类型自动转换为指针类型,因而传的实际是地址。创建一个数组,数组名实际为一指针变量,指向该数组的起始地址。 +2. 运行测试的结果为段错误。这是因为p是形参,p相当于str指针的复制,在函数内部new申请新内存后将内存地址赋给p并不会改变str指针的值。故str指针仍为NULL,则在将数据复制到str指针指向的地址时会段错误。 +3. 运行结果为一串未知字符,这是因为函数内部申请的变量为局部变量,其作用域仅限于函数执行过程中,在函数执行结束后局部变量即这里的p指向的栈空间中的内存区域就会被释放,返回值只是p指针的拷贝,指向原来p指向的地址。此时返回的指针指向的地址中的数据是不确定的,故可能会打印出一串未知字符或出现错误。 +4. 会正常打印出“你好世界”。 +5. 会打印出“世界”,因为使用new在堆上分配了内存后,str指向分配的这片内存,即str保存了这片内存的起始地址,而使用delete清理这片内存只是回收了这片内存空间,并没有将str指针重置。str仍然指向这片内存空间,则str并非空指针,strcpy可以正常的复制字符串,printf也可以正常打印输出。 + + +## 第三次作业 {#第三次作业} + +1. **使用I/O流以文本方式建立一个文件test1.txt,写入字符"已成功写入文件!",用其他字处理程序(例如Windows记事本程序Notepad)打开,看看是否正确写入。** + 本题比较简单,只需掌握C++中基本的I/O输入输出方式即可,这里重要的是掌握C++中流的概念。这一部分可以参考 _C++ Prime Plus(第6版)中文版_ 第17章相关内容。简单理解流相当于管道,在文件(硬件也可视为文件)和程序之间传递字节数据。因此文件流相关的类fstream对应的重载如<<和标准输入输出流iostream中的用法类似。则代码如下 + + ```C++ + #include + #include + using namespace std; + + int main() { + fstream iofile("test1.txt", ios::out); + iofile << "已成功写入文件!"; + cout << "写入成功"; + iofile.close(); + } + ``` + +编译运行如下 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1004jRF7OH.png) +查看文件内容如下 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1004TxWH43.png) + +1. **声明一个dog类,包含体重合年龄两个成员变量及相应的成员两数.声明一个实例dog1.体重为5,年龄为10,使用I/O流把dog1的状态写入磁盘文件,再声明另一个实例dog2,通过读文件把dog1的状态赋给dog2。分别使用文本方式和二进制方式操作文件,看看有何不同:再看看磁盘文件的ASCI码有何不同。** + +这个题只是在第一个题的基础上结合面向对象编程,简单的声明一个类并构造两个成员函数,一个用于修改对象的信息,一个用于打印对象的信息。在打开文件流的时候用两种不同的方式打开文件流即可。 + +```C++ +#include +#include +#include +#include +using namespace std; + +class dog { +public: + int weight; + int age; + + void change_attr(int x1, int x2) { + weight = x1; + age = x2; + } + + // 打印dog信息 + void print() { cout << "weight: " << weight << " age: " << age << endl; } + + dog() {} +}; + +int main() { + string line; + dog dog1; + dog1.change_attr(5, 10); + + // 文本模式 + fstream doginfo("./test2.txt", ios::out); + // cout << doginfo.is_open(); + // 写入文件 + doginfo << dog1.weight << " " << dog1.age; + dog dog2; + doginfo.close(); + doginfo.open("./test2.txt", ios::in); + getline(doginfo, line); + stringstream ss; + // 读取文件内容转换成字符串流并读入 + string wei; + string ag; + ss << line; + ss >> wei >> ag; + int x1 = stoi(wei), x2 = stoi(ag); + dog2.change_attr(x1, x2); + dog2.print(); + doginfo.close(); + + // 二进制模式 + doginfo.open("test3.txt", ios::out | ios::binary); + // 写入文件 + doginfo << dog1.weight << " " << dog1.age; + doginfo.close(); + doginfo.open("test3.txt", ios::in | ios::binary); + getline(doginfo, line); + ss << line; + ss >> wei >> ag; + x1 = stoi(wei); + x2 = stoi(ag); + dog2.change_attr(stoi(wei), stoi(ag)); + dog2.print(); + doginfo.close(); +} +``` + +运行结果为 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1007fXhkIJ.png) +用文本编辑器打开test2.txt和test3.txt得 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1007LKkmym.png) +这里并没有显示出什么差异主要是因为对于可见字符来说,使用二进制方式和文本方式差异不大,但是对于一些不可见字符如文件的文件头等,二进制读取方式会将数据原封不动的读取出来,而文本则会处理为文本后读取,因为txt文件除了编码类型外无文件头,所以二者没什么区别,但对于其他有格式文件来说可能就会存在区别,另外,以文本方式打开时,遇到结束符CTRLZ(0x1A)就认为文件已经结束。所以,若使用文本方式打开二进制文件,就很容易出现文件读不完整,或內容不对的错误。即使是用文本方式打开文本文件,也要谨慎使用,比如复制文件,就不应该使用文本方式。 + +1. **编写程序提示用户输入一个十进制整数.分别用十进制、八进制、和十六进制形式输出。** + +对进制进行转换可以使用短除反取余的方式,除对应进制数然后反向取余数即可。但是如果用int 或者long int类型存储输入的十进制数并进行转换,能转换的数字大小是有限的,在考虑可能有大整数的情况下,使用字符串存储并进行大整数的模数运算,从而实现了大整数的进制转换。该程序可以输入任意大的整数,都可以完成进制转换。 + +```C++ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +// 字符转数字 +int ctoi(char num) { return num - '0'; } + +// 8进制大整数转换 +pair big_mod_eight(string now) { + string quo; + int remain; + int before = ctoi(now[0]); + if (now.length() == 1) { + remain = before % 8; + int result = before / 8; + quo = to_string(result); + return make_pair(quo, remain); + } + before = 0; + for (int i = 0; i < now.length(); i++) { + int number = before * 10 + ctoi(now[i]); + before = number % 8; + remain = number / 8; + quo = quo + to_string(remain); + } + if (quo[0] == '0') + quo.erase(quo.begin()); + return make_pair(quo, before); +} + +// 16进制大整数转换 +pair big_mod_hex(string now) { + string quo; + int remain; + // 只有一位直接返回 + if (now.length() == 1) { + remain = ctoi(now[0]); + quo = "0"; + return make_pair(quo, remain); + } + // 两位及以上进行处理 + int before = ctoi(now[0]); + for (int i = 1; i < now.length(); i++) { + int number = before * 10 + ctoi(now[i]); + if (number < 16) { + quo += "0"; + i++; + if (i < now.length()) + number = number * 10 + ctoi(now[i]); + } + before = number % 16; + remain = number / 16; + quo = quo + to_string(remain); + } + if (quo[0] == '0') + quo.erase(quo.begin()); + return make_pair(quo, before); +} + +int main(int argc, char *argv[]) { + + string interger; + vector convert; + + while (true) { + cout << "Please enter a decimal, and q for quit: "; + cin >> interger; + // interger = "23"; + string result; + int remain; + pair temp; + result = interger; + if (interger == "q") { + break; + } else { + cout << "Dec = " << interger; + while (result != "0") { + temp = big_mod_eight(result); + result = temp.first; + remain = temp.second; + convert.push_back(remain); + } + cout << " Oct = "; + // 将结果逆序 + reverse(begin(convert), end(convert)); + for (auto n : convert) { + cout << n; + } + convert.clear(); + + result = interger; + while (result != "0") { + temp = big_mod_hex(result); + result = temp.first; + remain = temp.second; + convert.push_back(remain); + } + cout << " Hex = "; + // 将结果逆序 + reverse(begin(convert), end(convert)); + for (auto n : convert) { + cout << hex << n; + } + cout << endl; + convert.clear(); + } + } + return 0; +} +``` + +运行结果图如下 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011Jy1fbS.png) + +1. **编写程序实现如下功能:打开指定的一个文木文件,在每一个行前加行号。** + +本题考察C++基本的文件IO和文件指针,比较方便的做法是用一个中间文件,首先读取原文件的内容,每读取一行加一个行号并写入到中间文件中,最后将加了行号的中间文件内容再写入原文件并删除中间文件。 + +```C++ +#include +#include +#include +#include +#include +#include +using namespace std; + +int main() { + cout << "Please input the filename you want to open: "; + string filename; + cin >> filename; + ifstream fin(filename); + string backup = filename + ".back_up"; + ofstream fout(backup); + int number = 1; + string sline; + while (true) { + getline(fin, sline); + if (fin.eof()) + break; + fout << number << " " << sline << endl; + number++; + } + fin.close(); + fout.close(); + fout.open(filename); + fin.open(backup); + while (true) { + getline(fin, sline); + if (fin.eof()) + break; + fout << sline << endl; + } + fin.close(); + fout.close(); + // 删除临时文件 + const char *filerename = backup.c_str(); + if (remove(filerename) == 0) { + cout << "Add line number success!"; + } else { + cout << "Remove backup file error!"; + } + return 0; +} +``` + +效果如下,原文件内容为 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011AbNMl5.png) +运行程序后,文件内容为 +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011d5ZAut.png) +![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011FgrC32.png) + +1. **定义一个保存学生信息的结构体STUDENT,包含的学生信息有:学号、姓名、专业和平均分。其中学号和平均分使用整型,姓名和专业使用宁符数组。使用动态数组存储学生信息,并编写菜单,实现学生信息的录入、删除和显示功能。由手录入学生的数量未知,因此要使用new运算符实现动态内存的分配、使用delete运算符实现动态内存的回收。另外,使用标准流对象cin和cout完𢦓数据的输入输出;使用函数重载(例如添加学生到数组时可以采用不同的参数列表、显示学生信息时可以指定成绩区间等)、默认形参、引用变量;以上功能实现在自定义的名字空间中(建议使用学号做名字空间)。** + +定义命名空间为zqy19281235,定义STUDENT结构体,定义添加学生函数,使用默认形参和引用变量。考虑到学号的唯一性,使用map模板来存储学生信息。并将map通过引用传入函数,通过学号删除学生信息并释放申请的内存,重载显示学生信息的函数,当不输入指定的成绩区间时默认全部显示,当输入指定成绩区间时可以仅指定最大值或者最小值,另一个使用默认形参进行处理。代码如下 + +```C++ +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +namespace zqy19281235 { +struct STUDENT { + int sno; + string name; + string specialty; + int average; +}; + +// 有默认形参,使用引用变量 +void add_stu(map &students, int sno, string name, + string specialty, int average = 60) { + struct STUDENT *new_stu = new (struct STUDENT); + new_stu->sno = sno; + new_stu->name = name; + new_stu->specialty = specialty; + new_stu->average = average; + students.insert(make_pair(sno, new_stu)); +} + +void del_stu(map &students, int sno) { + // 释放内存 + delete (students.find(sno)->second); + students.erase(sno); + cout << "Delete success!"; +} + +// 默认全部显示 +void look_up_stu(map &students) { + for (auto stu : students) { + cout << "Sno = " << stu.second->sno << " Name = " << stu.second->name + << " Specialty = " << stu.second->specialty + << " Average = " << stu.second->average << endl; + } +} + +// 重载的显示函数 +void look_up_stu(map &students, int flag, int min = 0, + int max = 100) { + for (auto stu : students) { + if (stu.second->average >= min && stu.second->average <= max) { + cout << "Sno = " << stu.second->sno << " Name = " << stu.second->name + << " Specialty = " << stu.second->specialty + << " Average = " << stu.second->average << endl; + } + } +} + +} // namespace zqy19281235 + +int main() { + char select; + cout << "Welcome to my student manage system!!!!!" << endl; + map students; + while (true) { + cout << "Please select the operation you want:" << endl + << "\t1.add a student.\n" + << "\t2.delete a student\n" + << "\t3.display students infomation\n" + << "\tq for quit\n" + << "your select:"; + cin >> select; + switch (select) { + case '1': { + int number, average; + string aver; + string name, specialty; + cout << "The student number:"; + cin >> number; + cout << "The student name:"; + cin >> name; + cout << "The student specialty:"; + cin >> specialty; + cin.get(); + cout << "The student average(can be nothing):"; + getline(cin, aver); + if (aver.length() == 0) { + zqy19281235::add_stu(students, number, name, specialty); + } else { + average = stoi(aver); + zqy19281235::add_stu(students, number, name, specialty, average); + } + break; + } + case '2': { + int number; + cout << "The student number:"; + cin >> number; + zqy19281235::del_stu(students, number); + break; + } + case '3': { + string min, max; + cout << "Enter the min average of the students:"; + cin.get(); + getline(cin, min); + cout << "Enter the max average of the students:"; + getline(cin, max); + if (min.length() == 0 && max.length() == 0) { + zqy19281235::look_up_stu(students); + break; + } else if (max.length() == 0) { + zqy19281235::look_up_stu(students, 1, stoi(min)); + } else if (min.length() == 0) { + zqy19281235::look_up_stu(students, 1, 0, stoi(max)); + } else { + zqy19281235::look_up_stu(students, 1, stoi(min), stoi(max)); + } + break; + } + case 'q': { + return 0; + } + default: + break; + } + } +} +``` + +功能展示如下: + +- 添加学生 + ![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012YB8d7l.png) +- 显示学生信息(无限制) + ![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012NSCVin.png) +- 显示学生信息(有限制) + ![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012JkQKxs.png) +- 删除学生信息 + ![](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012uB9SkP.png)