第一次上机作业

第一题

安装和配置C++开发环境

所使用开发环境

版本 Windows 11 家庭中文版
版本 22H2
安装日期 ‎2022/‎9/‎22
操作系统版本 22621.2283
体验 Windows Feature Experience Pack 1000.22662.1000.0

编译Compile

有如下选择

  • MinGW
  • MinGW-w64
  • MSVC

先前配置的是MinGW,个人认为为了满足学习需求,稳定性足够足矣,不过为了满足作业的完整性,我愿意再演示一下MinGW-w64的配置

ide

选择使用vsc(Visual Studio Code),这是一款轻量化的文本编辑器(我认为基本配置后可以称作ide),进行小的编辑使用比较方便,如果后续有更大的项目级开发需求,我会考虑换使用Visual Studio

所使用代码编辑器的截图

环境配置

先前提到,未配置过的vscode仅能作为一个文本编辑器使用,为了可以在ide上对代码进行语法分析编译运行调试等,需要安装相应的拓展,并在拓展中设置编译器配置信息

安装过程无需授权等,因为使用的内容均为免费的(如果追求开源可以寻找开源版vscode但是我认为没必要

配置过程

0x01: 下载

访问官方网站进行下载:https://www.mingw-w64.org/downloads/

下载页面

The heart of the Mingw-w64 project is headers and support libraries to run the output of GCC on Windows. Since Mingw-w64 is neither the home of GCC nor of binutils, several sets of installation packages which combine them are available.

In addition, the sources are available but most people will want to grab binaries directly.

大意就是这里准备了编译好的二进制文件供开发者跳过编译阶段直接使用编译好的文件;在这里,选择正确的平台和架构是非常重要的,因为你需要确定二进制文件是在自己所使用环境下兼容的;如果精通且追求安全,可以自行下载代码并编译自己平台下的二进制文件

点击右侧source处转到sourceforge下载二进制文件

选择版本

选择在线下载器

online downloader

下面是安装地址的选择以及是否创建快捷方式,直接使用默认,点击next

在下载时出现报错

推测是网络问题,如果未出错请跳过(

直接到sourceforge下载之前选定的文件最新版

随后解压到之前设置的目录中

下载完成

0x02: 配置环境变量

点击 win+s 搜索:环境变量

先到之前安装mingw-w64的目录中进入到bin目录,随后复制目录地址

然后配置到系统环境变量Path中

点击所有确定后,打开powershell进行验证

win+s搜索powershell

gcc -v


(这里是之前qt环境的gcc,删除该环境变量后重启powershell再看)

我们可以看到例如win32,gcc版本8.1.0之类的基本信息,一切正常,环境配置完成

0x03: 应用配置到ide
  • 为什么要使用ide?
  • ide(Integrated Development Environment)会自动为你检查项目中的语法错误,关联上下文分析你用到的变量是否初始化、是否为私有变量、调用的方法返回什么类型的值、指针的检查等等,它还会自动在编译时跳过繁琐的编译命令

严格的来说,空架子的vsc仅可称作一个代码编辑器,但通过安装各种插件支持,使得它的集成度提高(例如分析各种类型的编程语言语法,编译和调试该语法,使用远程开发插件或安装wsl的支持使得其可以用于跨平台开发等等),使其可以满足编程开发的需求的同时,保持了它的轻量化(前提是开发者乐意花心思去整理明白自己的开发环境)

下载vsc:https://code.visualstudio.com/

直接点击Download for Windows下载stable版本
(本文跳过安装步骤)

下载后点击左侧拓展搜索和下载(注意发布者)

随后尝试创建和编译运行第一个cpp源代码

在这个目录下,我们创建了第一个cpp文件example.cpp,随后尝试点击右上角使用g++编译和运行

1
2
3
4
5
6
7
#include<iostream>

int main(){

std::cout << "A good start";
return 0;
}

右上角点击运行

选择安装好的编译器

运行结果:

可以看到,打印了 A good start 输出!

同时可以点击调试控制台查看调试信息

至此,ide配置完成,开发环境初步配置完成


第二题

C++实现合并排序算法

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<iostream>
#include<vector>

void divide(std::vector<int>& arr, int start, int end){

void merge(std::vector<int>& arr, int start, int middle, int end);

// 检查长度
if (start>=end) return;

int middle = start + (end - start) / 2;
divide(arr, start, middle);
divide(arr, middle + 1, end);
merge(arr,start,middle,end);
}

void merge(std::vector<int>& arr, int start, int middle, int end){
int n1 = middle - start + 1;
int n2 = end - middle;

// 创建临时数组来存放两个子数组
std::vector<int> leftArr(n1);
std::vector<int> rightArr(n2);

for (int i = 0; i < n1; i++) {
leftArr[i] = arr[start + i];
}
for (int j = 0; j < n2; j++) {
rightArr[j] = arr[middle + 1 + j];
}

// 合并两个子数组
int i = 0, j = 0, k = start;
while (i < n1 && j < n2) {
if (leftArr[i] <= rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
k++;
}

// 处理剩余元素
while (i < n1) {
arr[k] = leftArr[i];
i++;
k++;
}
while (j < n2) {
arr[k] = rightArr[j];
j++;
k++;
}
}

int main(){
int n; // 用于存储数组的长度
std::cout << "请输入数组的长度: ";
std::cin >> n;
std::vector<int> arr(n); // 创建一个包含n个元素的vector

std::cout << "请输入 " << n << " 个整数: ";
for (int i = 0; i < n; i++) {
std::cin >> arr[i];
}

std::cout << "你输入的数组是: ";
for (int i = 0; i < n; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;

divide(arr, 0, arr.size()-1);

std::cout << "排序后: ";
for (int i = 0; i < n; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}

截图

第三题

编写函数判断一个整数是否是质数,在主程序中实现输入输出

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <cmath>

bool isPrime(int number) {
if (number <= 1) {
return false; // 1和负数不是质数
}

for(int i = 2; i <= sqrt(number); i++){
if(number%i==0) return false;
}
return true;
}

int main() {
long long number;
std::cout << "请输入一个整数: ";
std::cin >> number;

if (isPrime(number)) {
std::cout << number << " 是质数" << std::endl;
} else {
std::cout << number << " 不是质数" << std::endl;
}

return 0;
}

截图

prime

第四题

通过递归的形式,计算菲波那切数列第 n 项。 探索递归调用下,最大能计算到第多少
项?并分析程序继续求解的原因。

源码

在使用long long类型的时候发生了变量溢出,故增加计数和高精度计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <string>
#include <algorithm>

long long count=0;

std::string addLargeNumbers(const std::string& num1, const std::string& num2) {
std::string result;
int carry = 0;

int i = num1.length() - 1;
int j = num2.length() - 1;

while (i >= 0 || j >= 0 || carry > 0) {
int digit1 = (i >= 0) ? num1[i] - '0' : 0;
int digit2 = (j >= 0) ? num2[j] - '0' : 0;

int sum = digit1 + digit2 + carry;
carry = sum / 10;
result += std::to_string(sum % 10);

i--;
j--;
}

std::reverse(result.begin(), result.end());
return result;
}

void fibonacci(std::string a, std::string b){
std::cout<<addLargeNumbers(a,b)<<" count:"<<++count<<std::endl;
fibonacci(b,addLargeNumbers(a,b));
}

int main(){
fibonacci("1","1");
return 0;
}

截图

可见在第12949次递归中发生栈溢出(递归次数是固定的,因为分配的栈内存是固定大小的)

stack overflow

分析

一次又一次的函数调用会多次申请栈空间,当栈内存饱和,地址分配光了之后便会导致栈溢出报错 stack overflow

编译器会尽量优化代码(例如将可以尾递归的递归调用函数按照尾递归的方式优化(省去了创建变量储存返回内容的内存))

1
2
3
4
5
6
7
8
9
10
11
int get(int x){
if (x >= 114514) return x; //在x=114514时返回x
int res = get(x+1);
return res;
}

// 优化后
int get(int x){
if (x >= 114514) return x; //在x=114514时返回x
return get(x+1); //可见,减少了一个int res的内存占用
}

第五题

Complex 类:设计一个名为 Complex 的类,表示一个复数,这个类包括:

  • 两个 double 型数据成员: x 和 y,分别表示复数的实部和虚部;
  • 一个带形参的构造函数,用于创建指定实部和虚部的复数;
  • 成员函数 getx(),获取实部;
  • 成员函数 gety(),获取虚部;
  • 成员函数 Display(),在屏幕上输出一个复数,如 2+3i, 4‐5i;
  • 成员函数 Abs(),返回一个复数的模;
  • 成员函数 Minus(Complex &),返回当前复数与指定复数之差;
  • 成员函数 Multiply(Complex &),返回当前复数与指定复数的乘积;

实现这个类,并在主函数中测试这个类:创建两个复数 z1=1.4‐2.3i 和 z2=‐3.5+2.7i,
并在屏幕上输出 z1 的模, z1‐z2 和 z1×z2 的值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <cmath>

class Complex{
private:
float realPart;
float imaginaryPart;

public:
Complex(double real, double imag) : realPart(real), imaginaryPart(imag) {}
float getx() const{return realPart;}
float gety() const{return imaginaryPart;}
void Display(){
std::cout<<realPart;
if(imaginaryPart==0)return;
if(imaginaryPart>0){
std::cout <<"+";
}
std::cout <<imaginaryPart<<"i";
}
float Abs() const{return sqrt(realPart * realPart + imaginaryPart * imaginaryPart);}

Complex Minus (Complex &other) const{
return Complex(realPart-other.realPart,imaginaryPart-other.imaginaryPart);
}
Complex Multiply(const Complex& other) const {
return Complex(realPart * other.realPart - imaginaryPart * other.imaginaryPart, realPart * other.imaginaryPart + imaginaryPart * other.realPart);
}

//实现符号重载
Complex operator+(const Complex& other) const {
return Complex(realPart + other.realPart, imaginaryPart + other.imaginaryPart);
}
Complex operator-(const Complex& other) const {
return Complex(realPart - other.realPart, imaginaryPart - other.imaginaryPart);
}
Complex operator*(const Complex& other) const {
return Complex(realPart * other.realPart - imaginaryPart * other.imaginaryPart, realPart * other.imaginaryPart + imaginaryPart * other.realPart);
}
friend std::ostream& operator<<(std::ostream& out, const Complex& c) {
out << c.realPart;
if(c.imaginaryPart==0)return out;
if (c.imaginaryPart > 0) {
out << "+";
}
out << c.imaginaryPart << "i";
return out;
}
};

int main(){
Complex z1 = Complex(1.4,-2.3);
Complex z2 = Complex(-3.5, 2.7);

std::cout<<"z1: "<<z1<<std::endl;
std::cout<<"z2: "<<z2<<std::endl;

std::cout<<z1.Abs()<<std::endl;
std::cout<<z1-z2<<std::endl;
std::cout<<z1*z2<<std::endl;
return 0;
}

截图

complex
在类中已经重载运算符

有点好奇为什么getx和gety要小写开头难道是private方法吗但是这样的话直接获取变量就好了呀