用多个文件制作和编译C 项目

Making and compiling C++ projects with multiple files

本文关键字:编译 项目 文件      更新时间:2023-10-16

我是C/C 的新手,我已经知道基础知识,现在开始学习更多高级概念。

目前,我正在使用C 开发一个项目,这是一个很大的项目,因此结构我的项目会更好。

从我已经看到的内容来看,一个好的结构至少依赖于文件夹:/src/include。所有.CPP文件都应在/src文件夹中,并在/include中的.HPP中使用。首先出现了第一个疑问:如何包括一个不在同一目录或标准目录中的标头?在搜索它之后,我得出的结论是,最好的方法是将选项-I../include传递给编译器。

我有三个源文件:main.cpp,graphdata.cpp和randomgraphgenerator.cpp。我读到,为每个文件中的每个文件都有一个标题,将声明与定义分开是最好的实践。现在我也有两个标题,但是我没有得到如何编译所有这些。

我正在专注于将graphdata.cpp正确链接到main.cpp。这是我的主要文件:

// main.cpp
#include <iostream>
#include <string>
#include <Snap.h>
#include <GraphData.hpp>
int main() {
    std::string file_path;
    char path[1024];
    DataPoint **data_set;
    int motif_size, num_null_models;
    std::cin >> file_path;
    std::cin >> motif_size;
    std::cin >> num_null_models;
    data_set = new DataPoint*[num_null_models];
    strncpy(path, file_path.c_str(), sizeof(path));
    path[sizeof(path) - 1] = 0;
    //read input
    PNGraph G = TSnap::LoadEdgeList<PNGraph>(path, 0, 1);
    //enumerate and classify k-subgraphs in G
    GD::extractData(&G, motif_size, data_set[0]);
    //generate random graphs and enumerate and calssify k-subgraphs on them
    for(int i=1; i<=num_null_models; i++) {
        //GM::randomize(&G);
        GD::extractData(&G, motif_size, data_set[i]);
    }
    //detect motifs
    GD::discoverMotifs(data_set);
    return 0;
}

我的GraphData标头:

#ifndef GRAPHDATA_HPP_INCLUDED
#define GRAPHDATA_HPP_INCLUDED
#include <Snap.h>
struct DataPoint;
namespace GD {
    void extractData(PNGraph*, int, DataPoint*);
    DataPoint* discoverMotifs(DataPoint**);
}
#endif // GRAPHDATA_HPP_INCLUDED

和我的GraphData文件:

#include <GraphData.hpp>
struct DataPoint {
    int label;
    int frequency = 0;
};
void GD::extractData(PNGraph* G, int k, DataPoint* data_array) {
    //stuff
}
DataPoint* GD::discoverMotifs(DataPoint** data_set) {
    //dummy code
    DataPoint* dp;
    dp = new DataPoint[2];
    dp[0].label = 10;
    dp[1].label = 99;
    return dp;
}

我试图在Ubuntu 14.10下使用GCC 4.9.2进行编译,使用:

g++ -o main main.cpp /usr/include/Snap-2.3/snap-core/Snap.o -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core

,但它给了我一个未定义的推荐的错误:

main.cpp:(.text+0x179): undefined reference to `GD::extractData(TPt<TNGraph>*, int, DataPoint*)'
main.cpp:(.text+0x1b9): undefined reference to `GD::extractData(TPt<TNGraph>*, int, DataPoint*)'
main.cpp:(.text+0x1dd): undefined reference to `GD::discoverMotifs(DataPoint**)'
collect2: error: ld returned 1 exit status

我对此有些迷失,我想念什么?

g 默认情况下,一旦完成了所有输入文件,就会立即调用链接器(LD)。生产二进制可执行文件的方法有很多,但是最常见的两个方法是:

  1. 将所有源文件包括在一个g++命令中。您需要将GraphData.cppmain.cpp添加到g++的同一调用中。此方法可确保链接器可以访问所有符号以创建可执行文件。
  2. 将每个文件分别编译到对象文件中,然后在单独的步骤中链接它们(对于大型软件项目而言,这更常见)。为此,请将-c开关传递到每个CPP文件的g++,然后再次致电G 并传递.o文件而不是.cpp文件。

无论哪种方式,在某个时候,您都必须一次将所有符号提供给链接器,以创建可执行文件。

正如约阿希姆(Joachim)所解释的那样,几个文件以外的任何事物都会变得巨大的痛苦,此时学习构建系统可能是非常有利可图的。他提到了制造,但我也考虑研究具有更简单的语法并且是跨平台的Cmake。它甚至可以自动生成Makefiles

[注意:这不是问题的答案,这只是关于如何一个一个一个一个汇编多个文件的解释]

you can 分别编译源文件,但是您必须告诉g++创建对象文件,然后将其链接在一起。

类似的东西:

$ g++ -Wall -g -c -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core main.cpp
$ g++ -Wall -g -c -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core GraphData.cpp
$ g++ -g  main.o GraphData.o /usr/include/Snap-2.3/snap-core/Snap.o -o main

选项-Wall告诉g++启用更多警告,这是很好的,因为警告是您可能做错事的迹象(例如引起 undfined行为)。-g选项告诉g++生成调试信息,如果您需要在调试器中运行该程序,则在开发过程中始终可以使用。最后,-c选项是告诉g++生成对象文件而不是可执行程序的方法。

然后,您有一个单独的命令,将对象文件链接在一起以形成最终可执行文件。

如果您收到了多个文件,那么这一切都会在背面痛苦,然后您应该了解make,这是一个可以为您自动化的程序。例如,它将检查文件上的时间戳记,以确保您不编译未修改的文件。