avatar

目录
338 - cpp Build

338 - C++ Build

🧑‍💻 User

In this lecture, we’re going to revisit our C plus plus compilation model.Along the way, we’re going to see that we can split our compilation process in two steps, and thatis compilation and linking.And we’re going to see that that’s going to be very important for the topics we’re going to talk aboutin this chapter.We already know that to compile our program, we’re going to pass that through the compiler.So what we usually do, we issue a compilation command or program is passed through the compiler.The compiler is going to do its magic and it is going to generate the binary that we can run to do things.But we want to zoom in on this compilation process and see what is happening behind the curtains.So when we compile our program, the first thing that is going to happen is pre-processing and our sourcecode is going to be passed through the preprocessor and the things like include statements and otherthings are going to be processed to be put in our translation unit or CPP files in our C plus plus program.After pre-processing, we have a version of our code which is really not what we typed and we’re goingto pass that through the compiler again to generate object files.So after compilation we’re going to get object files and these are going to be binary representationsof our source code.After that, we’re going to do what we call linking.And in this process we’re going to stitch together all the object files we have in our program, andat the end result is going to be one binary executable that we can run on our system.So what we want to do in this lecture is try this out.We’re going to compile our program in steps and see our object files and really get a feel of what thesethings really are.Okay, So now our compilation model has been updated.We have three steps, we have pre-processing, we have compilation, we have linking.But in this lecture, what we’re going to be looking at is really compilation and linking because pre-processingis really not that important for what we’re going to talk about in this chapter.To compile our program, we’ll be doing our thing on the terminal, so we will be using a terminal likethis.But you can really use any terminal.As long as it works, there is no problem.And you should know that Ides or editors like Visual Studio Code actually call compilers using the sametechniques we’re going to use here.They just provide a more convenient way to do this.So this should be a good lecture to also get a feel of what is happening behind the scenes.As your editor or IDE is doing its thing, We’re going to see how to do this using GCC because it isour main compiler in the course, but the same concepts apply to any other C plus plus compiler youmay be using in your career as a C plus plus developer.You may be using the compiler from Microsoft MVC, you may be using Clang.Any C plus plus compiler can really do this.It is a matter of searching and finding out how to do this thing.We will be using a simple program which is going to be split into three files.We’re going to have a utilities header file.We’re going to have a utilities CPP file, which is going to contain some functions that we want tocall and we will have a main CPP file which is going to contain our main function here.So if we want to compile our program, we can do it in one go, as you see here.And the command to do this is what you see here.We’re going to call GCC, which is our compiler.We’re going to pass a flag, which is going to mean that we want to compile and link.We’re going to specify the output file, which is going to be our program router dot exe.And we’re going to specify the source files.If we hit enter with this command, this is going to compile and link the program and the output ofthis is going to be an executable that we can run directly, but we can also do this in two steps.Remember, we want to break this into compilation and linking so that we can see the object files thatthe compiler generates.And it is really crucial to understand this.So what we want to do is to break this in two steps.We’re going to compile only and the output of compilation is going to be a pair of object files.The command to do that is what you see here.We’re going to call the compiler.We’re going to pass a C flag and then we’re going to specify our input files.The output of this is going to be a pair of object files.Now that we have the object files, we can really do anything we want with them.For example, we can open them and see what is inside.We’re going to do that in a minute, but the ultimate goal is really to stitch these object files togetherand get the final binary file and the command to do that is what you see here.We’re going to link up the object files or stitch them together to get the output file.And what we’re going to do is.Say G plus plus, we’re going to specify all and then we’re going to specify input to the compiler.The input is going to be a pair of object files, and our object files will be the files that we gotfrom the last step when we compiled our program only.And if you do this, you’re going to be able to split your compilation in two steps.The first one is going to be compilation, which is going to generate object files.The second one is going to be the linking stage, which is going to take object files as input and itis going to generate a final binary file that you can run in your system as a word of caution to beable to do what we are about to do.Your compiler should be in your path, so you should be able to call it from anywhere on your terminal.And if it is in your path, you’re going to have no problem with what we are about to do in this coursehere.Now that you know this, we’re going to head over to Visual Studio code and play with this a littlemore.Okay.So here we are in our working folder.The current project is compiling and linking and compilation model here.We’re going to grab our template files and we’re going to copy them.We’re going to put them in place and we are going to open this little guy in Visual Studio code.We are going to set up our project so we have the main file here.Let’s clean up whatever we don’t need and we’re going to add a pair of utilities or functions that aregoing to be living in a separate file.So we’re going to add a utilities header file.Let’s do that.And we are going to add a CPP file for this.The header file is going to contain a pair of functions.So we’re going to be adding up and multiplying stuff and we’re going to put implementations of theseguys in the CPP file.Okay, so let’s do that.We have the implementation for the Add function and an implementation for the multiply function here.We’re going to head over to the main function and include our utilities, file the header.That’s what we want and we are going to head over in the main function and try to use these functionshere.So we’re going to set up a result variable which is going to be of double type and it’s going to addup maybe ten and 20.Why not?And after we do this, we’re going to print the result out.We’re going to say Stdcout, we’re going to say result, and we’re going to print the result out here.Now the program is working.If we build it with Visual Studio Code or wherever it is going to work.But what we really want is to see how to do these things on the terminal and eventually be able to breakour compilation in two steps.The first step is going to be compilation, and the second step is going to be linking and we want tosee the object files that are generated after we compile and generate those object files here.So let’s do this.We’re going to bring up a terminal.Okay, so this terminal is working and this current directory, if we do LS, we’re going to find ourfiles on windows.You can also do dir to see what is inside your folder.We’re going to see that we have the main CPP file.We have our utilities file, the header and the CPP file and we can compile them to be able to compilewith GCC, you need to be able to do G plus plus here and do version.If we do this, we’re going to see our G plus plus compiler.And if you see a message like this, you can call the compiler and do what we are about to do here.If you can’t call your compiler, please make sure you have this in your environment variables.So if we do EMF on our start menu here and hit edit system environment variables, we’re going to chooseenvironment variables here.If you go to your path, you should be able to find the path to the bin directory where g plus plusis installed.If I go in my C drive, let’s do that.I am going to bring up a window and we’re going to go to my C drive.I am going to go in a main GW if I can find it, can I find it?We have it here.Main GW 64.I am going to go and I am going to go in the same directory that I have here in my environment variable.And if I go in this folder you’re going to see that I have my G plus plus compiler right here.So you should have the same setup here to be able to call this little guy from a terminal.So make sure you have this in and you will be able to do what we are about to do.We have seen that it is possible to compile and link our program in one go using the command we seehere.So we’re going to head over to the terminal and do this.So what we’re going to do is call G plus plus we’re going to say G plus plus.We’re going to specify the O flag to mean that we want to compile and link.We’re going to specify the output file, which is going to be rooster dot exe.Then we’re going to specify our input files.We’re going to have the main.File.We have that n and we have our utilities file.If we hit enter, this is going to compile our program.If we do LS, we’re going to find a rooster dot exe file.And if we run it, we’re going to see that this is going to run.It is going to say result is 30.And in our main CPP file, this is what we are doing.We are adding up ten and 20 and the result is going to be 30.Let’s change the input here and make sure that we are really running this program here.We’re going to say 20.8 here and we’re going to compile and link again.So let’s bring this command up again and we’re going to compile and link if we do.LS You’re going to see our rooster exe file.If we run it, we’re going to get a different result.So our program is working here.We can compile and link it on the terminal in one go.Now let’s do a less and we’re going to remove Rooster dot exe here and we want now to compile and linkin two steps.The first step is going to be compilation.The second step is going to be linking the command to compile only is what you see here.So we’re going to say G plus plus specify a C flag and then we’re going to say our input as our mainCPP file and utilities CPP file.Let’s head back over to Visual Studio code and do that.We’re going to say G plus plus specify our C flag and then we’re going to say main CPP file and utilitiesCPP file.If we hit enter, this is going to run.If we do.LS Now we see that we have a main dot o file and a utilities dot o file.If we open this up in our file system, let’s do that.We’re going to find in Explorer and then we’re going to find our main dot o file.We can try and open it with an editor and you’re going to see that it’s some gibberish because thisis a binary file.It is meant to be processed by computers.It is not meant to be read in editors like this.So this is really cool.We are able to generate an object file.Now what we want is to use this object files and generate a binary file and the command to do that iswhat you see here.We’re going to link up our object files.We’re going to call the compiler, we’re going to specify our flag, we’re going to specify the outputfile, and then we’re going to specify our input files to be main dot o and utilities that oh, let’shead over to Visual Studio code and do this.Okay, So we are back in Visual Studio code.We’re going to say G plus plus and we’re going to specify our O flag.We’re going to specify the output to be rooster dot x and our input is going to be main dot o and utilitiesdot o, which are our object files.If we run enter, we’re going to see that this is going to run.If we do LS we’re going to see that we have a rooster dot x file and we were able to generate this fromour object files.If we run Rooster, we’re going to see that we get the exact same result we got before.So this is really how you can compile and link in two steps using the G plus plus compiler that comeswith GCC.Hopefully this is going to shed light on how some things are done and you have to note that functioncalls are resolved at link time.For example, if we try to call this function here and we don’t have a function definition here, competitionis going to work because the compiler is really interested in declarations and our declarations willbe brought in by the enclosure that we are doing here.So the compilation is going to work, but linking is going to fail.Let’s try and do that.We’re going to comment out our implementations.Let’s do that so that they are disabled and we’re going to try and compile this program.Again, we’re going to clear and notice that we are calling the Add function in main.This is what is going to cause problems here.If we try to compile the program and let’s go up and see if we can find our compilation command, let’sdo LS and remove all the clutter that was generated.We’re going to remove main dot O, We’re going to remove the utilities that O file and we’re going toremove rooster dot exe.We’re going to clear and run LS and we’re going to see that we have our source code here.This is really cool.So we’re going to go up and find our compilation command.We want to compile this program and generate our object files.If we do this, you’re going to see that compilation is going to work and we’re going to generate ourobject files here.But if we try to link them, we’re going to get a problem.Let’s try and do that.We’re going to bring back.The command to link.I think we have it in our history here.We have that here.If we run this, you’re going to see that we have a problem undefined reference to add.This is something you can understand now because function calls are resolved at link stage and the compilerdoesn’t really care that we don’t have definitions and the compiler is just happy.Looking at the declarations here.And this is something I wanted you to see in this lecture, this is really all I had to share in thislecture, showing you that you can compile your program on the terminal and break things in two steps.The first step is compilation, the second one is linking.And now you know that function calls are resolved at the link stage.And if you don’t have your definitions and you’re going to have undefined references, as we see here,we’re going to stop here in this lecture.And the next one, we’re going to shed some light on declarations and definitions and learn a littlemore about these important concepts in C plus plus.Go ahead and finish up here and meet me there.


🤖 Assistant

好的,很高兴能为您揭开C++编译过程的神秘面纱!作为您的C++导师,我将尽力用最清晰、最易懂的方式来讲解这些核心概念。


C++编译模型深度解析:理解编译与链接

摘要: 本讲将深入探讨C++程序从源代码到可执行文件的转换过程,重点聚焦于编译(Compilation)和链接(Linking)这两个关键阶段。通过分步操作和实际代码示例,您将亲身体验生成目标文件(Object Files)并最终链接成可执行文件的全过程,从而彻底理解“未定义引用”(Undefined Reference)等常见链接错误的原因。


1. C++程序构建流程:从源代码到可执行文件

要运行您编写的C++程序,计算机需要将其转换为它能理解的二进制指令。这个转换过程就是编译模型(Compilation Model)。通常,我们运行一个命令,编译器(Compiler)会“施展魔法”,直接生成一个可运行的二进制文件。但今天,我们将“放大”这个过程,看看幕后究竟发生了什么。

一个完整的C++程序构建过程通常包括三个主要步骤:

  1. 预处理(Pre-processing)

    • 作用: 这是编译过程的第一步。预处理器(Preprocessor)会处理源代码中以#开头的指令,例如#include(包含头文件)、#define(定义宏)等。
    • 细节: 当您使用#include <iostream>时,预处理器会找到iostream头文件的内容,并将其完整地粘贴到您的源代码文件中。同样,如果您定义了宏,预处理器会用宏的定义替换所有宏的使用。
    • 输出: 预处理的输出是一个翻译单元(Translation Unit),这是一个纯C++代码文件,不再包含任何预处理指令,但包含了所有被#include进来的代码和宏展开后的内容。这个文件通常很大,因为它包含了所有依赖的头文件内容。
    • 重要性: 虽然预处理非常重要,但本次课程我们将更侧重于编译和链接,因为它们与我们在大型项目中组织代码的方式以及解决“未定义引用”问题更为相关。
  2. 编译(Compilation)

    • 作用: 编译器(Compiler)将预处理后的翻译单元转换为机器语言指令。但请注意,此时生成的代码还不是一个完整的可执行程序。
    • 细节: 编译器会检查您的C++代码的语法(Syntax)和语义(Semantics)是否正确。它会将您的C++代码翻译成汇编语言(Assembly Language),然后再将汇编语言翻译成机器码。在这个阶段,编译器主要关注声明(Declarations),即某个函数或变量“存在”的承诺。如果它看到一个函数调用,只要这个函数被声明了(例如在头文件中),编译器就会认为这是有效的,并为这个调用生成相应的机器码。然而,它并不知道这个函数的具体实现(定义,Definitions)在哪里。
    • 输出: 编译的输出是目标文件(Object Files),通常以.o(在Linux/macOS上)或.obj(在Windows上)作为文件扩展名。目标文件是二进制文件,包含了您的源代码编译后的机器码,但它们是不完整的,因为它们可能包含对其他文件中定义的函数或变量的引用(Call/Reference),这些引用需要在后续阶段才能被解析。
  3. 链接(Linking)

    • 作用: 链接器(Linker)将所有编译生成的目标文件,以及任何所需的库文件(Libraries),“缝合”在一起,形成一个完整的、可运行的程序。
    • 细节: 这是解决“未定义引用”问题的关键阶段。当编译器生成目标文件时,它知道您调用了一个函数,但不知道这个函数的具体实现代码在哪里。链接器的工作就是找到这些缺失的函数或变量的定义。它会在所有目标文件和您指定的库文件中寻找这些定义,并将它们连接起来。如果链接器找不到某个函数或变量的定义,就会报错,通常是“Undefined Reference”(未定义引用)。
    • 输出: 链接的最终输出是一个可执行文件(Executable File),或者是一个动态/静态库文件。这个文件可以直接在您的操作系统上运行。

IDE(集成开发环境)与终端
像Visual Studio Code、Visual Studio、CLion等IDE,实际上是自动化了上述在终端(Terminal)中进行的编译和链接命令。它们提供了一个更便捷、图形化的界面来管理项目和执行这些操作,但底层调用的原理和命令是相同的。学习如何在终端操作,能让您对IDE幕后发生的事情有更深入的理解。

编译器选择
本课程将使用GCC(GNU Compiler Collection)中的G++编译器作为示例。GCC是一个非常流行的开源编译器,在Linux、macOS和Windows(通过MinGW或Cygwin)上都广泛使用。但请注意,无论您使用Microsoft的MSVC编译器(通常与Visual Studio集成)还是Clang编译器,其核心编译和链接的概念都是一致的,只是具体的命令参数可能有所不同。

环境配置(PATH变量):
要在终端中直接调用g++命令,您需要确保编译器的安装路径已添加到系统的PATH环境变量(Environment Variables)中。PATH变量告诉操作系统在哪里可以找到可执行程序。如果g++不在PATH中,系统将无法识别该命令。您可以通过在终端输入g++ --version来检查它是否已正确配置。

2. 实际代码示例:分步编译与链接

我们将使用一个简单的C++程序,它被拆分成三个文件:

  • utilities.h:头文件,包含函数声明。
  • utilities.cpp:源文件,包含函数定义。
  • main.cpp:主程序文件,包含main函数并调用utilities中的函数。

程序代码:

utilities.h:

cpp
1
2
3
4
5
6
7
8
#ifndef UTILITIES_H
#define UTILITIES_H

// 函数声明 (Function Declarations)
double add(double a, double b);
double multiply(double a, double b);

#endif // UTILITIES_H

utilities.cpp:

cpp
1
2
3
4
5
6
7
8
9
10
#include "utilities.h" // 包含头文件,获取函数声明

// 函数定义 (Function Definitions)
double add(double a, double b) {
return a + b;
}

double multiply(double a, double b) {
return a * b;
}

main.cpp:

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include "utilities.h" // 包含我们自己的头文件

int main() {
double num1 = 10.0;
double num2 = 20.8;

// 调用 utilities.cpp 中定义的函数
double sum_result = add(num1, num2);
double product_result = multiply(num1, num2);

std::cout << "Sum: " << sum_result << std::endl;
std::cout << "Product: " << product_result << std::endl;

return 0;
}

现在,我们来看如何在终端中进行编译和链接。

2.1 一步到位:编译与链接一次完成

这是我们最常见的方式。编译器会自动处理预处理、编译和链接所有源文件:

命令:

bash
1
g++ main.cpp utilities.cpp -o my_program.exe

  • g++: 调用G++编译器。
  • main.cpp utilities.cpp: 指定需要编译的源文件。
  • -o my_program.exe: -o 标志用于指定输出文件的名称。在这里,输出将是一个名为my_program.exe的可执行文件(在Linux/macOS上通常没有.exe扩展名,直接是my_program)。

执行结果:
执行此命令后,如果没有错误,您的目录下会生成my_program.exe文件。运行它,您将看到计算结果:

bash
1
2
3
./my_program.exe
Sum: 30.8
Product: 208

2.2 分步进行:先编译,后链接

为了更好地理解目标文件和链接过程,我们可以将上述一步操作拆分为两步。

步骤一:仅编译(生成目标文件)

我们分别编译main.cpputilities.cpp,各自生成一个目标文件:

命令:

bash
1
2
g++ -c main.cpp
g++ -c utilities.cpp

  • -c: 这个标志告诉编译器“只编译,不链接”。这意味着它会生成目标文件,但不会尝试创建可执行文件。

执行结果:
执行这些命令后,您的目录下会生成两个新的文件:

  • main.o (或 main.obj)
  • utilities.o (或 utilities.obj)

这些就是我们说的目标文件。如果您尝试用文本编辑器打开它们,会发现它们是乱码(”gibberish”),因为它们是机器码的二进制表示,不是给人阅读的。它们包含了您的函数和变量的机器码,但可能还包含对其他目标文件中定义的函数(如addmultiplymain.o中对utilities.o中函数的引用)的未解析引用

步骤二:仅链接(将目标文件“缝合”成可执行文件)

现在我们有了目标文件,我们可以使用链接器将它们组合起来,创建最终的可执行文件:

命令:

bash
1
g++ main.o utilities.o -o my_program.exe

  • g++: 再次调用G++,但这次它主要作为链接器使用(因为它没有-c标志,并且输入是.o文件)。
  • main.o utilities.o: 指定作为链接器输入的目标文件
  • -o my_program.exe: 指定输出的可执行文件名称。

执行结果:
执行此命令后,链接器会将main.outilities.o中的机器码拼接在一起,解析所有内部引用,最终生成my_program.exe。运行结果与一步到位时相同。

通过这个分步过程,您可以看到main.outilities.o是如何产生的,以及它们在链接阶段被组合成一个完整程序的。

3. 常见误解与错误(以及非代码示例)

3.1 误解:编译就是生成最终的可执行文件

  • 澄清: 正如我们所见,编译仅仅是将源代码转换为目标文件。这些目标文件是中间产物,它们还不能独立运行。只有经过链接阶段,才能生成最终的可执行文件。
  • 非代码示例:想象您要制作一个复杂的乐高城堡。
    • 预处理:打开所有乐高零件包,清点并分类零件。
    • 编译:根据图纸,您会分别搭建城堡的各个独立部分:一个塔楼、一段城墙、一个大门。这些独立搭建好的部分,就像是编译阶段生成的目标文件。每个部分都是独立的,但它们本身还不是完整的城堡。
    • 链接:最后,您将所有这些独立搭建好的塔楼、城墙、大门等部分(目标文件)连接起来,按照总图纸将其组合成一个完整的、可玩的乐高城堡。这个最终的城堡,就是可执行文件。如果某个部分在设计时需要连接到另一个“理论上存在”但你还没建好的部分,那在连接时就会出问题(链接错误)。

3.2 误解:所有错误都是编译错误

  • 澄清: C++程序中会遇到两种主要的错误:

    • 编译错误:通常是语法错误(如缺少分号、拼写错误)、类型不匹配、未声明的变量或函数(因为编译器在编译时找不到其声明)。这些错误会在g++ -c阶段或一步到位的g++命令的编译阶段被检测到。
    • 链接错误:最典型的就是“undefined reference”(未定义引用)。这发生在链接阶段,意味着编译器在编译时看到了某个函数或变量的声明(知道它应该存在),但链接器在所有目标文件和库中都找不到它的定义(实际的实现代码)。
  • 案例分析:undefined reference

    • 情景重现: 假设在utilities.cpp中,我们注释掉了add函数的实现(定义),但在main.cpp中仍然调用了add函数。
    • 编译阶段
      • g++ -c main.cpp:会成功。因为main.cpp包含了utilities.h,其中有add函数的声明,所以编译器知道add函数存在。它会生成main.o
      • g++ -c utilities.cpp:也会成功。即使add函数没有实现,编译器也只是编译utilities.cpp有效的代码。它会生成utilities.o
    • 链接阶段
      • g++ main.o utilities.o -o my_program.exe:此时会报错!链接器在处理main.o时发现它引用了add函数,然后它会在utilities.o和其他默认库中寻找add函数的定义,但因为utilities.cpp中的add定义被注释掉了,所以链接器找不到,从而报出undefined reference to 'add(double, double)'这样的错误。

这个例子清楚地说明了编译器只关心声明,而链接器关心定义的重要性。

3.3 忘记链接所有必要的目标文件或库

  • 常见错误: 有时候,当我们项目文件很多时,可能会忘记在链接命令中包含所有编译生成的目标文件。
  • 后果: 如果你只链接了main.o,而main.o调用了utilities.o中的函数,你仍然会得到undefined reference错误,因为链接器找不到utilities.o中那些函数的定义。
  • 解决方案: 确保在链接命令中包含了所有您的项目所需的目标文件,以及任何外部库文件(例如,如果您的程序使用了数学函数,可能需要链接到数学库,如-lm)。

4. QA 闪存卡(Flash Cards)

  1. Q: C++程序从源代码到可执行文件通常要经过哪三个主要步骤?
    A: 预处理(Pre-processing)、编译(Compilation)、链接(Linking)。

  2. Q: g++ -c 命令的作用是什么?它会生成什么文件?
    A: -c 标志表示“只编译,不链接”。它会将源代码文件编译成目标文件(Object Files,如.o.obj),这些文件是二进制的中间产物,不能直接运行。

  3. Q: 编译阶段和链接阶段分别关注什么?
    A:

    • 编译阶段主要关注代码的语法和声明(Declarations)的正确性。
    • 链接阶段主要关注解析所有定义(Definitions)的引用,将不同的目标文件和库文件组合成一个完整的可执行程序。
  4. Q: 当您看到“undefined reference”错误时,这意味着什么?它发生在哪个阶段?
    A: 这意味着链接器找不到某个函数或变量的定义(实现)。它发生在链接阶段。通常是因为对应的源文件没有被编译,或者编译后的目标文件没有被包含到链接命令中,或者该定义确实不存在。

  5. Q: 为什么IDE(如Visual Studio Code)能直接“运行”C++代码而无需手动输入g++命令?
    A: IDE在幕后自动化了编译和链接过程。它们会根据您的项目配置,自动调用相应的编译器(如g++)并传递正确的参数来完成编译和链接,然后运行生成的可执行文件。

5. 编码练习:修复未定义引用

现在,请您完成下面的C++代码。您的任务是修复一个常见的错误:未定义引用

背景:
我们有一个calculator.h头文件,声明了一个subtract函数。main.cpp试图调用这个函数。

文件结构:

  • calculator.h (已提供)
  • main.cpp (已提供,但会出错)
  • calculator.cpp (需要您创建并实现 subtract 函数)

calculator.h:

cpp
1
2
3
4
5
6
7
#ifndef CALCULATOR_H
#define CALCULATOR_H

// 函数声明
double subtract(double a, double b);

#endif // CALCULATOR_H

main.cpp:

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "calculator.h" // 包含函数声明

int main() {
double val1 = 100.0;
double val2 = 35.5;

// 尝试调用 subtract 函数
double diff = subtract(val1, val2); // 这一行会导致问题!

std::cout << "Difference: " << diff << std::endl;

return 0;
}

您的任务:

  1. 创建 calculator.cpp 文件。
  2. calculator.cpp 中实现 subtract 函数。 该函数应该接受两个double类型的参数,并返回它们的差。
  3. 使用 g++ 命令编译和链接这些文件,使其能够成功运行。 您可以选择一步到位或分步进行。

请在您的回复中提供 calculator.cpp 的完整代码,以及您使用的 g++ 编译命令。



评论