如何在使用g++时调用另一个文件中定义的函数?

3

这是一个非常基础的问题,我在网上找到了很多概念性的答案,但实际上无法使其工作。

这是我的代码:

文件 source.cc

#include <iostream>

int myfunc() {
  return 42;
}

int main() {
  return 0;
}

然后我通过以下方式创建一个对象文件source.o:

g++ -c source.cc

最终,我使用了。
ar rvs source.a source.o

获取源代码,一个静态库。

现在,问题来了。

文件 user.cc 的内容如下:

#include <iostream>
#include <source.a>

int main() {
  std::cout << myfunc();
}

我显然想要使用库中定义的函数,但在尝试编译user.cc时出现了问题:

g++ user.cc -o user

我收到的错误信息是:

user.cc:2:22: fatal error: source.a: No such file or directory
compilation terminated.

尖括号通常不会搜索当前目录,但这并不重要。我非常怀疑你想包含一个 .a 文件。链接到它即可。 - chris
你不是*包含(include)一个库,而是链接(link)*一个库。你还需要函数的声明(通常放在头文件中)。你可能还会遇到链接一个同时定义了main的库的问题。 - crashmstr
7个回答

3

#include 是编译时必须使用的 C/C++ 头文件(不是库),包含例如这个内容。

extern int myfunc();

如果你想将所有文件编译在一起(在命令行上指定需要的所有文件),那么你必须使用链接器。


1
删除 extern,它是多余的。 - Piotr Skotnicki
但我认为这是一个好的实践,因为你需要它来定义变量。把它放在那里并不会有什么坏处,而且你也不必考虑哪里需要它,哪里不需要。 - firda
@firda:老实说,使用extern导出全局变量对我来说似乎不是一个很好的做法...如果你真的需要共享某种形式的全局状态,你可能想要导出一些getter/setter 函数。我同意在C++中,导出函数的extern是多余的,应该被删除:我不喜欢它,就像我不喜欢在C++中使用foo(void),而更喜欢更简单、更清晰的foo() - Mr.C64
我确实使用 foo() 并重写旧代码中的任何 foo(void),但我不能同意使用 getter,为什么?这为什么是不好的做法?那全局的 shared_mutex 呢?那是一个完美的例子,你更喜欢几个返回 lock_guardunique_lockshared_lock 或其他东西的 getter 吗? - firda

2
在C++(以及C99)中,每个你调用的函数都必须事先声明。为此,你需要提供该函数的“签名”,而不包括定义。在你的情况下,这将是以下语句:
int myfunc();

这段代码告诉编译器myfunc是一个不带参数并返回一个int的函数。通常情况下,你需要在头文件中加入这个函数声明。

.a文件是一个已编译的归档文件,它不包含C或C++代码,因此无法通过将其#include到C++文件中来使用。相反,您需要创建一个C或C++头文件,并将.a归档文件添加到链接到最终可执行文件的文件列表中。使用g++很容易实现,你可以这样说:

g++ user.cc source.a -o executable

例如。


2
除了其他人已经在命令行上正确使用g++的语法之外,您可能还需要考虑以下关于代码组织的注释。
在您的库代码中,不应定义main()函数。相反,main()函数的定义应该是使用您的库的代码的一部分,例如示例中的user.cc文件。
此外,您可能希望向库的客户分发一个头文件,他们可以使用该文件导入库导出的函数的声明。
因此,请考虑定义一些类似以下结构的文件:
头文件:
// library.h -- Public header for your library's clients

#pragma once   // or use #ifndef/#define/#endif "header guards"

// Functions exported by your library: 
// their *declarations* go in the library's public header file;
// their *definitions* go in the library's implementation file(s) (.cc, .cpp)
// (exception: inline functions/methods, that are implemented in headers).

int myfunc();

// Add some other exported functions...

// NOTE: "extern" not needed in C++!

实现文件:

// library.cc -- Library implementation code
#include "library.h" // library public header 
#include <...>       // headers required by this implementation code

// *Define* functions exported by the library

int myfunc() {
    return 42;
}

// ...other function implementations...

然后,库的客户端只需执行以下操作:
包含main()并使用您的库的文件:
// main.cc (or user.cc or whatever you call it)

#include <iostream>   // For std::cout, std::endl
...#include any other required header file...

#include "library.h"  // Your library public header file

int main() {
    // Call library's function
    std::cout << myfunc() << std::endl;
}    

// NOTE: main() is special: "return 0;" can be omitted.

非常感谢,这对我解决了许多问题!现在我已经有了头文件source.h和源文件source.cc,我已经将main.cc编译为main.o,但我是否仍然需要链接库? - miha priimek
@mihapriimek:很高兴能帮到你。顺便说一句,作为一个好的Stackoverflow用户,你可能想要给所有对你有帮助的答案点赞(这个帖子里有几个),并将最佳答案标记出来。 - Mr.C64
当然,我只是试图通过尝试人们建议的不同方法来找出哪一个对我最有帮助。(在点赞某个特定答案之前) - miha priimek
我可以问一下在库中包含库头文件的意义吗?没有它也能工作。 此外,"" 是否意味着在当前目录中搜索? - miha priimek
也许你的情况非常简单,但一般情况下,库头文件中可以定义结构和类,并且它们可以在库实现代码中引用和使用,因此需要在library.cc文件中包含库头文件。 - Mr.C64

2
不要使用#include引入库存档文件,因为它不包含源代码,而是包含目标代码。应该将其放在链接器的命令行中。
使用g++ -c user.cc进行编译。 使用g++ -o user user.o source.a进行链接。

0

默认情况下,#include <...> 结构(带有尖括号)会搜索系统目录以查找指定的文件。要搜索其他目录,您可以使用 -L 选项,并且要在命令行中使用 source.a 链接您的库。像这样:

g++ user.cc -L/path/to/library source.a -o user

0
几个想法可供讨论: 1)你应该创建一个名为source.h的头文件,其中包含以下行: int myfunc(); 或者, extern int myfunc();
2)在user.cc的顶部应该有一行 include "source.h" 这会告诉编译器函数已定义。
3)我认为你应该将main函数从source.cc中取出,或者至少使其成为静态的。
正如其他人所指出的那样,你不能包含库文件(source.a) 你的编译和链接过程应该按照现有方式进行。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接