hugo/content/posts/bj_homework.md
2022-10-12 12:43:19 +08:00

16 KiB
Raw Blame History

+++ 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. 会正常打印出“你好世界”。
  4. 会打印出“世界”因为使用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中的用法类似。则代码如下

    #include <fstream>
    #include <iostream>
    using namespace std;
    
    int main() {
      fstream iofile("test1.txt", ios::out);
      iofile << "已成功写入文件!";
      cout << "写入成功";
      iofile.close();
    }
    

编译运行如下

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1004jRF7OH.png" >}}

查看文件内容如下

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1004TxWH43.png" >}}

  1. 声明一个dog类包含体重合年龄两个成员变量及相应的成员两数声明一个实例dog1.体重为5年龄为10使用I/O流把dog1的状态写入磁盘文件再声明另一个实例dog2通过读文件把dog1的状态赋给dog2。分别使用文本方式和二进制方式操作文件看看有何不同再看看磁盘文件的ASCI码有何不同。

这个题只是在第一个题的基础上结合面向对象编程,简单的声明一个类并构造两个成员函数,一个用于修改对象的信息,一个用于打印对象的信息。在打开文件流的时候用两种不同的方式打开文件流即可。

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
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();
}

运行结果为

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1007fXhkIJ.png" >}}

用文本编辑器打开test2.txt和test3.txt得

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1007LKkmym.png" >}}

这里并没有显示出什么差异主要是因为对于可见字符来说使用二进制方式和文本方式差异不大但是对于一些不可见字符如文件的文件头等二进制读取方式会将数据原封不动的读取出来而文本则会处理为文本后读取因为txt文件除了编码类型外无文件头所以二者没什么区别但对于其他有格式文件来说可能就会存在区别另外以文本方式打开时遇到结束符CTRLZ(0x1A)就认为文件已经结束。所以,若使用文本方式打开二进制文件,就很容易出现文件读不完整,或內容不对的错误。即使是用文本方式打开文本文件,也要谨慎使用,比如复制文件,就不应该使用文本方式。

  1. 编写程序提示用户输入一个十进制整数.分别用十进制、八进制、和十六进制形式输出。

对进制进行转换可以使用短除反取余的方式除对应进制数然后反向取余数即可。但是如果用int 或者long int类型存储输入的十进制数并进行转换能转换的数字大小是有限的在考虑可能有大整数的情况下使用字符串存储并进行大整数的模数运算从而实现了大整数的进制转换。该程序可以输入任意大的整数都可以完成进制转换。

#include <algorithm>
#include <atomic>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
using namespace std;

// 字符转数字
int ctoi(char num) { return num - '0'; }

// 8进制大整数转换
pair<string, int> 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<string, int> 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<int> convert;

  while (true) {
    cout << "Please enter a decimal, and q for quit: ";
    cin >> interger;
    // interger = "23";
    string result;
    int remain;
    pair<string, int> 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;
}

运行结果图如下

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011Jy1fbS.png" >}}

  1. 编写程序实现如下功能:打开指定的一个文木文件,在每一个行前加行号。

本题考察C++基本的文件IO和文件指针比较方便的做法是用一个中间文件首先读取原文件的内容每读取一行加一个行号并写入到中间文件中最后将加了行号的中间文件内容再写入原文件并删除中间文件。

#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
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;
}

效果如下,原文件内容为

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011AbNMl5.png" >}}

运行程序后,文件内容为

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011d5ZAut.png" >}}

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1011FgrC32.png" >}}

  1. 定义一个保存学生信息的结构体STUDENT包含的学生信息有学号、姓名、专业和平均分。其中学号和平均分使用整型姓名和专业使用宁符数组。使用动态数组存储学生信息并编写菜单实现学生信息的录入、删除和显示功能。由手录入学生的数量未知因此要使用new运算符实现动态内存的分配、使用delete运算符实现动态内存的回收。另外使用标准流对象cin和cout完𢦓数据的输入输出使用函数重载(例如添加学生到数组时可以采用不同的参数列表、显示学生信息时可以指定成绩区间等)、默认形参、引用变量;以上功能实现在自定义的名字空间中(建议使用学号做名字空间)。

定义命名空间为zqy19281235定义STUDENT结构体定义添加学生函数使用默认形参和引用变量。考虑到学号的唯一性使用map模板来存储学生信息。并将map通过引用传入函数通过学号删除学生信息并释放申请的内存重载显示学生信息的函数当不输入指定的成绩区间时默认全部显示当输入指定成绩区间时可以仅指定最大值或者最小值另一个使用默认形参进行处理。代码如下

#include <cstdio>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
using namespace std;

namespace zqy19281235 {
struct STUDENT {
  int sno;
  string name;
  string specialty;
  int average;
};

// 有默认形参,使用引用变量
void add_stu(map<int, struct STUDENT *> &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<int, struct STUDENT *> &students, int sno) {
  // 释放内存
  delete (students.find(sno)->second);
  students.erase(sno);
  cout << "Delete success!";
}

// 默认全部显示
void look_up_stu(map<int, struct STUDENT *> &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<int, struct STUDENT *> &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<int, zqy19281235::STUDENT *> 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;
    }
  }
}

功能展示如下:

  • 添加学生

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012YB8d7l.png" >}}

  • 显示学生信息(无限制)

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012NSCVin.png" >}}

  • 显示学生信息(有限制)

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012JkQKxs.png" >}}

  • 删除学生信息

{{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1012uB9SkP.png" >}}