iso*_*mes 4 c++ ubuntu gcc compilation include
我想我没有把事情联系好吗?
我想打电话给ABC.cpp哪些需求XYZ.h和XYZ.cpp.所有的都在我的当前目录下,我试着#include <XYZ.h>以及#include "XYZ.h".
$ g++ -I. -l. ABC.cpp在Ubuntu 10终端上运行给了我:
`/tmp/ccCneYzI.o: In function `ABC(double, double, unsigned long)':
ABC.cpp:(.text+0x93): undefined reference to `GetOneGaussianByBoxMuller()'
collect2: ld returned 1 exit status`
Run Code Online (Sandbox Code Playgroud)
这是ABC.cpp的摘要:
#include "XYZ.h"
#include <iostream>
#include <cmath>
using namespace std;
double ABC(double X, double Y, unsigned long Z)
{
...stuff...
}
int main()
{
...cin, ABC(cin), return, cout...
}
Run Code Online (Sandbox Code Playgroud)
这是XYZ.h:
#ifndef XYZ_H
#define XYZ_H
double GetOneGaussianByBoxMuller();
#endif
Run Code Online (Sandbox Code Playgroud)
这是XYZ.cpp:
#include "XYZ.h"
#include <cstdlib>
#include <cmath>
// basic math functions are in std namespace but not in Visual C++ 6
//(comment's in code but I'm using GNU, not Visual C++)
#if !defined(_MSC_VER)
using namespace std;
#endif
double GetOneGaussianByBoxMuller()
{
...stuff...
}
Run Code Online (Sandbox Code Playgroud)
我正在使用GNU Compiler版本g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3.
这是我的第一篇文章; 我希望我把所有需要知道的东西都包括在内帮助我.我实际上已阅读其中一个回复中列出的"相关问题"和高夫文章,并搜索了错误消息.但是,我仍然无法弄清楚它是如何应用于我的问题的.
提前致谢!
当你运行时,g++ -I. -l. ABC.cpp你要求编译器创建一个可执行文件ABC.cpp.但是此文件中的代码会回复定义的函数XYZ.cpp,因此由于缺少该函数而无法创建可执行文件.
您有两种选择(取决于您想要做什么).您可以一次性为编译器提供所有源文件,以便它具有所有定义,例如
g++ -I. -l. ABC.cpp XYZ.cpp
Run Code Online (Sandbox Code Playgroud)
或者,您使用-c选项compile to ABC.cpp来对象代码(Windows上的.obj,Linux中的.o),以后可以链接,例如
g++ -I. -l. -c ABC.cpp
Run Code Online (Sandbox Code Playgroud)
这将生成ABC.o哪些可以在以后链接XYZ.o以生成可执行文件.
编辑:#including和链接有什么区别?
完全理解这一点需要准确理解编译C++程序时会发生什么,不幸的是,很多人认为自己是C++程序员.在较高的层次上,C++程序的编译经历了三个阶段:预处理,编译和链接.
预处理
每个开头的行#都是一个预处理程序指令,它在预处理阶段进行评估.该#include指令实际上是一个复制粘贴.如果你写#include "XYZ.h",预处理器替换的全部内容该行XYZ.h(包括递归评估#include范围内XYZ.h).
包含的目的是使声明可见.为了使用该函数GetOneGaussianByBoxMuller,编译器需要知道它GetOneGaussianByBoxMuller是一个函数,并且要知道它所采用的参数(如果有的话)以及它返回的值,编译器将需要查看它的声明.声明放在头文件中,并包含头文件,以便在使用点之前使编译器可以看到声明.
编译
这是编译器运行的部分,将源代码转换为机器代码.请注意,机器代码与可执行代码不同.可执行文件需要有关如何将机器代码和数据加载到内存中的其他信息,以及如何在必要时引入外部动态库.这不是在这里完成的.这只是您的代码从C++到原始机器指令的部分.
与Java,Python和其他一些语言不同,C++没有"模块"的概念.相反,C++在翻译单元方面起作用.在几乎所有情况下,翻译单元对应于单个(非标题)源代码文件,例如ABC.cpp或XYZ.cpp.每个翻译单元都是独立编译的(无论是-c为它们运行单独的命令,还是一次性将它们提供给编译器).
编译源文件时,预处理器首先运行,并执行#include复制粘贴以及预处理器执行的宏和其他操作.结果是一个很长的C++代码流,包括源文件的内容和它包含的所有内容(以及它包含的所有内容等等).这长长的代码流是翻译单元.
编译翻译单元时,必须声明每个函数和使用的每个变量.编译器不允许你调用没有声明的函数或者使用没有声明的全局变量,因为它不会知道涉及的类型,参数,返回值等,也不会生成合理的代码.这就是你需要标题的原因 - 请记住,此时编译器甚至不知道是否存在任何其他源文件; 它只考虑由#include指令处理产生的这段代码.
在编译器生成的机器代码中,没有变量名或函数名这样的东西.一切都必须成为记忆地址.每个全局变量必须转换为存储它的内存地址,并且每个函数必须有一个内存地址,执行流程在调用时跳转到该内存地址.对于在转换单元中定义的事物(即,用于实现的功能),编译器可以分配地址.对于仅声明的内容(通常是包含头文件的结果)并且未定义,编译器此时不知道内存地址应该是什么.调用编译器仅具有声明但不具有定义/实现的这些函数和全局变量外部符号,并且假定它们存在于不同的翻译单元中.目前,它们的内存地址用占位符表示.
例如,当编译对应的翻译单元时ABC.cpp,它具有定义(实现)ABC,因此它可以为函数分配地址,ABC并且在ABC调用该翻译单元的任何地方,它可以创建到该地址的跳转指令.另一方面,尽管其声明是可见的,GetOneGaussianByBoxMuller但未在该翻译单元中实现,因此其地址必须用占位符表示.
编译翻译单元的结果是一个目标文件(.oLinux上有后缀).
链接
链接器的主要工作之一是解析外部符号.也就是说,链接器查看一组目标文件,查看它们的外部符号是什么,然后尝试找出应该为它们分配的内存地址,替换占位符.
在你的情况下,函数GetOneGaussianByBoxMuller被定义在对应的翻译单元XYZ.cpp,所以里面XYZ.o已经分配特定的内存地址.在对应的翻译单元中ABC.cpp,它只被声明,所以在里面ABC.o,它只是一个占位符(外部符号).链接器,如果同时给出ABC.o并且XYZ.o将看到ABC.o需要填写的地址,在其中GetOneGaussianByBoxMuller找到该地址XYZ.o,并ABC.o用它替换占位符.外部符号的地址也可以在库中找到.
如果链接器找不到地址GetOneGaussianByBoxMuller(就像它在你的例子中所做的那样ABC.o,由于没有传递XYZ.cpp给编译器),它将报告一个未解析的外部符号错误,也被描述为未定义的引用.
最后,一旦编译器解析了所有外部符号,它就会组合所有现在占位符的目标代码,添加操作系统所需的所有加载信息,并生成可执行文件.田田!
请注意,通过所有这些,文件的名称与一位无关.这是一个惯例,XYZ.h应该包含已定义的事物的声明XYZ.cpp,并且可维护的代码以这种方式组织事物是好的,但编译器和链接器不关心这是否真实.链接器将查看它给出的所有目标文件,并仅查看它给出的目标文件以尝试解析符号.它既不知道也不关心符号声明在哪个标题中,并且它不会尝试自动引入其他目标文件或编译其他源文件以解析丢失的符号.
......哇,那太久了.
尝试
g++ ABC.cpp XYZ.cpp
Run Code Online (Sandbox Code Playgroud)
如果要分别编译,则需要构建目标文件:
g++ -c ABC.cpp
g++ -c XYZ.cpp
g++ ABC.o XYZ.o
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3502 次 |
| 最近记录: |