编译包含结构的h文件时链接错误

时间:2013-11-22 09:33:51

标签: c++ struct mpi

我有这个ProcessStasts.h文件,该文件包含在另外两个.h文件中。

#pragma once

#include <mpi.h>
#include <cstddef>

struct ProcessStats
{
    int rank,
    itLeft,
    crtIt,
    processFlag;
    float speed;
};

MPI_Datatype MPI_Cust_ProcessStats_create()
{
    // set data to create new MPI data type
    MPI_Datatype MPI_Cust_ProcessStats;
    MPI_Datatype dataTypes[5] = {MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_FLOAT};
    int blockLengths[5] = {1, 1, 1, 1, 1};
    MPI_Aint offsets[5];

    offsets[0] = (MPI_Aint) offsetof(ProcessStats, rank);
    offsets[1] = (MPI_Aint) offsetof(ProcessStats, itLeft);
    offsets[2] = (MPI_Aint) offsetof(ProcessStats, crtIt);
    offsets[3] = (MPI_Aint) offsetof(ProcessStats, processFlag);
    offsets[4] = (MPI_Aint) offsetof(ProcessStats, speed);

    // create new MPI type based on data from above
    MPI_Type_create_struct(5, blockLengths, offsets, dataTypes, &MPI_Cust_ProcessStats);
    MPI_Type_commit(&MPI_Cust_ProcessStats);

    return MPI_Cust_ProcessStats;
}

当我尝试编译时,我收到此错误:error LNK2005: MPI_Cust_ProcessStats_create(void) already defined。如果我评论#include "ProcessStasts.h"指令和使用ProcessStats结构的行,则从其中一个文件中正确编译。我甚至试图评论依赖于ProcessStats的所有行,只留下#include "ProcessStasts.h"语句,我得到这个lnk错误。有什么问题?

2 个答案:

答案 0 :(得分:4)

您可以这样写: 首先是ProcessStasts.h

#pragma once

#include <mpi.h>
#include <cstddef>

struct ProcessStats
{
    int rank,
    itLeft,
    crtIt,
    processFlag;
    float speed;
};

MPI_Datatype MPI_Cust_ProcessStats_create();

然后是ProcessStasts.c

#include "ProcessStats.h"
MPI_Datatype MPI_Cust_ProcessStats_create()
{
    // set data to create new MPI data type
    MPI_Datatype MPI_Cust_ProcessStats;
    MPI_Datatype dataTypes[6] = {MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_FLOAT};
    int blockLengths[5] = {1, 1, 1, 1, 1};
    MPI_Aint offsets[5];

    offsets[0] = (MPI_Aint) offsetof(ProcessStats, rank);
    offsets[1] = (MPI_Aint) offsetof(ProcessStats, itLeft);
    offsets[2] = (MPI_Aint) offsetof(ProcessStats, crtIt);
    offsets[3] = (MPI_Aint) offsetof(ProcessStats, processFlag);
    offsets[4] = (MPI_Aint) offsetof(ProcessStats, speed);

    // create new MPI type based on data from above
    MPI_Type_create_struct(3, blockLengths, offsets, dataTypes, &MPI_Cust_ProcessStats);
    MPI_Type_commit(&MPI_Cust_ProcessStats);

    return MPI_Cust_ProcessStats;
}

然后,您可以根据需要多次包含ProcessStasts.h。作为建议,不要在头文件中定义函数。

答案 1 :(得分:2)

#pragma once指示预处理器不要包含两次头文件。这主要用于防止递归包含和多个间接包含,例如:

#include <a.h>  // a.h already includes b.h
#include <b.h>

#pragma once开头没有b.h,其内容将被包含两次,并可能导致重新定义某些符号。

在您的情况下发生的事情是完全不同的事情。默认情况下,C和C ++中的函数具有外部链接。这意味着如果您在文件foo()中定义了函数bar.c,然后将bar.c编译为目标文件bar.o,则目标文件将按名称导出全局符号foo(实际上C ++将装饰名称以支持重载),可以从其他目标文件访问(引用)该符号。现在,如果文件baz.c包含另一个函数foo()的定义(在C ++的情况下具有相同的签名),则目标文件baz.o也会导出名称为{的全局符号。 {1}}。当目标文件链接在一起以生成可执行文件时,链接器会尝试将每个符号解析为唯一的内存地址。但现在有一个问题:有两个符号foo,它们都有不同的地址。链接器(通常)不是通灵的,所以它只是给你一个关于符号重新定义的错误信息并终止。

C和C ++都提供了一种控制函数链接的机制。如果添加foo关键字,则函数符号不再是全局的,只对共享相同编译单元的代码可见。这些功能具有静态链接。这就是为什么在头文件中定义的函数几乎总是带有static关键字:

static

现在#pragma once #include <mpi.h> #include <cstddef> struct ProcessStats { int rank, itLeft, crtIt, processFlag; float speed; }; static MPI_Datatype MPI_Cust_ProcessStats_create() { ... } 只会在包含头文件的源文件中显示。

未经请求的建议:MPI_Cust_ProcessStats_create()前缀是为MPI API调用保留的。将它用于用户函数是一种糟糕的编程习惯,因为有些工具依赖于只有MPI调用以MPI_开头并且可能会混淆的事实。