Header Guards和LNK4006

时间:2010-01-20 23:02:58

标签: c++ linker-warning

我在标题

中定义了一个字符数组
//header.h
const char* temp[] = {"JeffSter"};

标头如果#defined受到保护并且在顶部有一次#pragma。如果这个头包含在多个地方,我得到一个LNK4006 - 已经在blahblah.obj中定义的char const * * temp。所以,我对此有几个问题

  1. 如果我有警卫,为什么会这样?我认为他们在第一次访问后阻止了标题的读入。
  2. 为什么此标题中的众多枚举还没有给出LNK4006警告?
  3. 如果我在签名前添加静态,我不会收到警告。这样做有什么意义。
  4. 有没有更好的方法来避免错误,但让我在标题中声明数组。我真的很讨厌只有一个cpp文件用于数组定义。

5 个答案:

答案 0 :(得分:14)

  

如果我有防护装置,为什么会发生这种情况?我认为他们阻止了首次访问后读取标题。

包含警卫确保在一个文件(翻译单位)中仅包含一次标题。对于包含标题的多个文件,您希望标题包含在每个文件中。

通过定义,而不是在头文件中使用外部链接(全局变量)声明变量,您只能在一次源文件中包含标头。如果在多个源文件中包含标题,则会有多个变量定义,这在C ++中是不允许的。

因此,正如您所知,为了上述原因,在头文件中定义变量是一个坏主意。

  

为什么此标题中的众多枚举还没有给出LNK4006警告?

因为它们没有定义“全局变量”,它们只是关于类型等的声明。它们不保留任何存储空间。

  

如果我在签名前添加静态,我不会收到警告。这样做有什么意义。

制作变量static时,它具有静态范围。该对象在定义它的转换单元(文件)之外是不可见的。所以,简单来说,如果你有:

static int i;

在标题中,您在其中包含标题的每个源文件都会获得一个单独的 int变量i,该变量在源文件之外是不可见的。这称为内部链接

  

有没有更好的方法可以避免错误,但是让我在标题中声明数组。我真的很讨厌只有一个cpp文件用于数组定义。

如果您希望数组是所有C ++文件中可见的一个对象,则应该执行以下操作:

extern int array[SIZE];

在头文件中,然后在所有需要变量array的C ++源文件中包含头文件。在源(.cpp)文件的一个中,您需要定义array

int array[SIZE];

您也应该在上面的源文件中包含标题,以便捕获由于标题和源文件不同而导致的错误。

基本上,extern告诉编译器“array已在某处定义,并且类型为int,大小为SIZE”。然后,您实际上只定义 array一次。在链接阶段,一切都很好地解决了。

答案 1 :(得分:4)

包含防护装置可以防止您重复将相同的标题包含在同一个文件中 - 但不能将其包含在不同的文件中。
发生的事情是链接器在多个目标文件中看到temp - 你可以通过使temp静态或将其放入未命名的命名空间来解决这个问题:

static const char* temp1[] = {"JeffSter"};
// or
namespace {
    const char* temp2[] = {"JeffSter"};
}

或者,您可以使用一个定义temp的源文件,并在标题中将其声明为extern:

// temp.cpp:
const char* temp[] = {"JeffSter"};

// header.h:
extern const char* temp[];

答案 2 :(得分:4)

  1. 标题保护与阻止整个程序中的多个定义完全无关。标头保护的目的是防止多个相同的头文件包含在相同的翻译单元(。cpp文件)中。换句话说,它们的存在是为了防止同一源文件中的多个定义。并且他们确实按照您的意图工作。

  2. 管理C ++中多定义问题的规则称为“一个定义规则”(ODR)。对于不同类型的实体,ODR的定义不同。例如,允许 types 在程序中具有多个相同的定义。他们可以(并且大多数具有)在每个使用它们的翻译单元中定义。这就是您的枚举定义不会导致错误的原因。

    具有外部链接的对象是一个完全不同的故事。它们必须在一个且仅一个翻译单元中定义。这就是为什么当您将头文件包含到多个翻译单元中时,temp的定义会导致错误。包含警卫无法阻止此错误。只是不要在头文件中定义具有外部链接的对象。

  3. 通过添加static,您可以为对象提供内部链接。这将使错误消失,因为从ODR的角度来看现在完全可以。但是这将在每个翻译单元中定义一个独立的temp对象,其中包含头文件。为了达到同样的效果,你也可以做到

    const char* const temp[] = { "JeffSter" }; 
    

    因为C ++中的const对象默认具有内部链接。

  4. 这取决于您是否需要具有外部链接的对象(即整个程序的一个对象)或具有内部链接的对象(每个翻译单元都是唯一的)。如果您需要后者,请使用static和/或额外const(如果适用于您),如上所示。

    如果您需要前者(外部链接),则应将非定义声明放入头文件

    extern const char* temp[];
    

    并将定义移动到一个且只有一个.cpp文件

    char* const temp[] = { "JeffSter" }; 
    

    头文件中的上述声明适用于大多数用途。但是,它将temp声明为未知大小的数组 - 不完整的类型。如果您希望将其声明为已知大小的数组,则必须手动指定大小

    extern const char* temp[1];
    

    并记住在声明和定义之间保持同步。

答案 3 :(得分:0)

我恭敬地反对在标题中定义变量的建议,因为我认为“从不”过于宽泛。尽管如此,让我进入这个主题的那一集为那些敢于这样做的人提供了警示。

作为对LNK4006警告原因的调查结果,我登陆了这个页面,调用了一个长期建立的数组,我刚从定义我的DLLMain例程的转换单元移动到大多数包含的私有头中。构成此库的翻译单元。在过去的11年里,我已经数百次编译了这个库,我以前从未见过这个警告。

在我读完这个页面后不久,我发现了错误的原因,即定义在保护块之外,保护模块中定义的其他所有其他定义DLLMain的内容,这是我通常收集所有内容的地方需要外部链接的内存块。正如预期的那样,移动防护区内的桌子消除了警告,只留下了两个与全新的外部链接表有关的问题。

外卖:你可以在标题中定义变量,它是放置常见块的好地方,但请注意你的警卫。

答案 4 :(得分:-1)

坚持......你正在混淆你的声明......你确实在你的头文件中说'char const * * temp'你有'const char * temp [] = {“JeffSter”};'。

参见{第3节}第6节“数组和指针”中的第6.1节,引用:

6.1:    I had the definition char a[6] in one source file, and in
    another I declared extern char *a.  Why didn't it work?

A:  In one source file you defined an array of characters and in the
    other you declared a pointer to characters.  The declaration
    extern char *a simply does not match the actual definition.
    The type pointer-to-type-T is not the same as array-of-type-T.
    Use extern char a[].

    References: ISO Sec. 6.5.4.2; CT&P Sec. 3.3 pp. 33-4, Sec. 4.5
    pp. 64-5.

这是问题的根源。匹配您的声明和定义。对不起,如果这听起来很生硬,但我无法注意到链接器告诉你的内容......

希望这有帮助, 最好的祝福, 汤姆。