【问题标题】:Link error while compiling a h file containing a struct编译包含结构的 h 文件时出现链接错误
【发布时间】:2013-12-07 03:06:29
【问题描述】:

我有这个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 错误。怎么了?

【问题讨论】:

  • struct中有5个字段时,为什么MPI结构数据类型构造函数3的第一个参数是?
  • 因为一个错误,用更多的变量改变了结构......与DataType数组[6]相同。谢谢,这让我看到了我以后会发现的错误。

标签: c++ struct mpi


【解决方案1】:

你可以这样写: 首先是 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。建议不要在头文件中定义函数。

【讨论】:

  • 是的,这解决了问题。但是我不明白为什么,因为#pragma once在那里
  • #pragma once 会在您编译一个源文件时得到帮助。但是对于不同的源文件在编译的时候也会做include操作。
【解决方案2】:

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

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

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

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

C 和 C++ 都提供了一种机制来控制函数的链接。如果添加 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_ 前缀保留用于 MPI API 调用。将它用于用户函数是一种不好的编程习惯,因为有些工具依赖于只有 MPI 调用以 MPI_ 开头的事实,并且可能会混淆。

【讨论】:

  • 您将如何处理这个问题,将函数移动到 .cpp 文件或添加静态文件,以及为什么(一种模式与另一种模式相比有何优势)?
  • 这是一个偏好问题。如果你把它放在一个单独的源文件中,它只编译一次。如果你把它放在头文件中,它会被编译很多次。如果将它保存在头文件中并且必须更改函数的代码,则必须重新编译包含头文件的每个文件。如果它在一个单独的文件中,只需重新编译它。
  • 有些人喜欢将小函数保留在头文件中,因为这些函数可以被优化编译器内联。如果函数在一个单独的翻译单元中,那么它会变得更难(但对于过程间优化之类的事情并非不可能)。
  • 我在stackoverflow上发现了这种创建MPI_Datatype的模式。如何使用此函数创建数据类型?
  • 这是更好的答案,因为它解释了错误的来源;这是关于联动的。请注意,声明函数 inline 也可以。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
  • 2022-06-11
  • 2021-12-20
相关资源
最近更新 更多