接头护罩和 LNK4006

发布于 2024-08-18 13:23:59 字数 420 浏览 8 评论 0原文

我在标头中定义了一个字符数组

//header.h
const char* temp[] = {"JeffSter"};

标头如果#define 受保护并且顶部有一个#pragma。如果此标头包含在多个位置,我会得到一个 LNK4006 - char const * * temp 已在 blahblah.obj 中定义。所以,我对此有几个问题:

  1. 如果我有防护措施,为什么会发生这种情况?我认为他们阻止了第一次访问后读入标题。
  2. 为什么此标头中的众多枚举没有同时给出 LNK4006 警告?
  3. 如果我在签名前添加 static,则不会收到警告。这样做有什么影响。
  4. 有没有更好的方法可以避免错误,但让我在标头中声明数组。我真的不想有一个仅用于数组定义的 cpp 文件。

I have a character array defined in a header

//header.h
const char* temp[] = {"JeffSter"};

The header if #defined guarded and has a #pragma once at the top. If this header is included in multiple places, I get an LNK4006 - char const * * temp already defined in blahblah.obj. So, I have a couple of questions about this

  1. Why does this happen if I have the guards in place? I thought that they prevented the header from being read in after the first access.
  2. Why do the numerous enums in this header not also give the LNK4006 warnings?
  3. If I add static before the signature, I don't get the warning. What are the implications of doing it this way.
  4. Is there a better way to do this that avoids the error, but lets me declare the array in the header. I would really hate to have a cpp file just for an array definition.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

桃酥萝莉 2024-08-25 13:23:59

如果我有守卫,为什么会发生这种情况?我认为他们阻止了第一次访问后读入标题。

包含防护确保标头仅在一个文件(翻译单元)中包含一次。对于包含标头的多个文件,您希望每个文件中都包含标头。

通过定义,与在头文件中声明具有外部链接(全局变量)的变量相反,您只能将头包含在一次源文件中。如果在多个源文件中包含头文件,就会出现一个变量的多个定义,这在C++中是不允许的。

因此,正如您所发现的,正是由于上述原因,在头文件中定义变量是一个坏主意。

为什么此标头中的众多枚举没有同时给出 LNK4006 警告?

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

如果我在签名前添加静态,我不会收到警告。这样做会产生什么影响。

当您将变量设为静态时,它具有静态作用域。该对象在定义它的翻译单元(文件)之外是不可见的。因此,简单来说,如果您有:

static int i;

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

有没有更好的方法可以避免错误,但让我在标头中声明数组。我真的不想有一个仅用于数组定义的 cpp 文件。

如果您希望数组成为所有 C++ 文件中可见的一个对象,您应该:

extern int array[SIZE];

在头文件中执行以下操作,然后将头文件包含在所有需要变量 array 的 C++ 源文件中。在一个源文件(.cpp)中,您需要定义数组

int array[SIZE];

您还应该在上述源文件中包含标头,以允许捕获由于标头和源文件中的差异而导致的错误。

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

Why does this happen if I have the guards in place? I thought that they prevented the header from being read in after the first access.

Include guards make sure that a header is included only once in one file (translation unit). For multiple files including the header, you want the header to be included in each file.

By defining, as opposed to declaring variables with external linkage (global variables) in your header file, you can only include the header in once source file. If you include the header in multiple source files, there will be multiple definitions of a variable, which is not allowed in C++.

So, as you have found out, it is a bad idea to define variables in a header file for precisely the reason above.

Why do the numerous enums in this header not also give the LNK4006 warnings?

Because, they don't define "global variables", they're only declarations about types, etc. They don't reserve any storage.

If I add static before the signature, I don't get the warning. What are the implications of doing it this way.

When you make a variable static, it has static scope. The object is not visible outside of the translation unit (file) in which it is defined. So, in simple terms, if you have:

static int i;

in your header, each source file in which you include the header will get a separate int variable i, which is invisible outside of the source file. This is known as internal linkage.

Is there a better way to do this that avoids the error, but lets me declare the array in the header. I would really hate to have a cpp file just for an array definition.

If you want the array to be one object visible from all your C++ files, you should do:

extern int array[SIZE];

in your header file, and then include the header file in all the C++ source files that need the variable array. In one of the source (.cpp) files, you need to define array:

int array[SIZE];

You should include the header in the above source file as well, to allow for catching mistakes due to a difference in the header and the source file.

Basically, extern tells the compiler that "array is defined somewhere, and has the type int, and size SIZE". Then, you actually define array only once. At link stage, everything resolves nicely.

伴梦长久 2024-08-25 13:23:59

包含防护可以防止您将相同的标头重复包含到同一个文件中 - 但不能将其包含到不同的文件中。
发生的情况是链接器在多个目标文件中看到 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[];

Include guards protect you from including the same header into the same file repeatedly - but not from including it in distinct files.
What happens is that the linker sees temp in more then one object file - you can solve that by making temp static or putting it into an unnamed namespace:

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

Alternatively you can use one source file which defines temp and just declare it as extern in the header:

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

// header.h:
extern const char* temp[];
暗喜 2024-08-25 13:23:59
  1. 标头防护与防止整个程序中存在多个定义完全无关。标头防护的目的是防止同一标头文件多次包含到同一翻译单元(.cpp 文件)中。换句话说,它们的存在是为了防止同一源文件中存在多个定义。它们确实按照您的情况按预期工作。

  2. C++ 中管理多重定义问题的规则称为单一定义规则 (ODR)。 ODR 对于不同类型的实体有不同的定义。例如,类型允许在程序中具有多个相同的定义。它们可以(而且通常必须)在使用它们的每个翻译单元中定义。这就是为什么您的枚举定义不会导致错误。

    具有外部链接的对象是一个完全不同的故事。它们必须在一个且仅一个翻译单元中定义。这就是当您将头文件包含到多个翻译单元时,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];
    

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

  1. Header guards have absolutely nothing to do with preventing multiple definitions in your entire program. The purpose of header guards is to prevent multiple inclusion of the same header file into the same translation unit (.cpp file). In other words, they exist to prevent multiple definitions in the same source file. And they do work as intended in your case.

  2. The rule that governs multiple-definition issues in C++ is called One Definition Rule (ODR). ODR is defined differently for different kinds of entities. For example, types are allowed to have multiple identical definitions in the program. They can (and most always have to) be defined in every translation unit where they are used. This is why your enum definition does not result in an error.

    Objects with external linkage are a completely different story. They have to be defined in one and only one translation unit. This is why your definition of temp causes an error when you include the header file into multiple translation units. Include guards can't prevent this error. Just don't define objects with external linkage in header files.

  3. By adding static you give your object internal linkage. This will make the error disappear, since now it is perfectly OK from ODR point of view. But this will define an independent temp object in each translation unit into which your header file is included. To achieve the same effect you could also do

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

    since const objects in C++ have internal linkage by default.

  4. This depends on whether you need an object with external linkage (i.e. one for the entire program) or an object with internal linkage (unique to each translation unit). If you need the latter, use static and/or extra const (if that works for you) as shown above.

    If you need the former (external linkage), you should put a non-defining declaration into the header file

    extern const char* temp[];
    

    and move the definition into one and only one .cpp file

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

    The above declaration in the header file will work for most purposes. However, it declares temp as an array of unknown size - an incomplete type. If you wish to declare it as an array of known size, you have to specify the size manually

    extern const char* temp[1];
    

    and remember to keep it in-synch between the declaration and definition.

星軌x 2024-08-25 13:23:59

我非常不同意反对在标头中定义变量的建议,因为我认为“从不”太宽泛。尽管如此,让我想到这个话题的那一集给那些敢于这样做的人提供了一个警示。

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

读完本页后不久,我发现了错误的原因,即该定义位于保护块之外,该保护块保护模块中定义的所有其他内容,该模块也定义了 DLLMain,这是我通常收集所有内存块的地方需要外部链接。正如预期的那样,将表移动到防护块内消除了警告,只剩下两个与全新的外部链接表相关的警告需要解决。

要点:您可以在标头中定义变量,这是放置公共块的好地方,但请注意您的守卫。

I respectfully disagree with the advice against defining variables in headers, because I think "never" is too broad. Nevertheless, the episode that brought me to this thread offers a cautionary tale for those who dare to do so.

I landed on this page as the result of an inquiry into the cause of warning LNK4006, calling out a long established array that I just moved from the translation unit that defines my DLLMain routine into the private header that is included in most of the translation units that comprise this library. I have compiled this library hundreds of times over the last 11 years, and I had never before seen this warning.

Shortly after I read this page, I discovered the cause of the error, which was that the definition was outside the guard block that protects everything else that is defined in the module that also defines DLLMain, which is where I usually gather all the memory blocks that need external linkage. As expected, moving the table inside the guard block eliminated the warnings, leaving me with only two, related to a brand new externally linked table, to be resolved.

Takeaway: You can define variables in headers, and it's a great place to put common blocks, but mind your guards.

贪恋 2024-08-25 13:23:59

等一下...你混淆了你的声明...你确实说了'char const * * temp',但在你的头文件中你有'const char* temp[] = {"JeffSter"};'。

请参阅 C FAQ 的第 6.1 节,在“第 6 节。数组和指针”下,引用

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.

:问题的根源。匹配您的声明和定义。抱歉,如果这听起来很直率,但我忍不住注意到链接器告诉你的内容......

Hang on... you are mixing up your declarations...you did say 'char const * * temp' yet in your header file you have 'const char* temp[] = {"JeffSter"};'.

See section 6.1 of the C FAQ, under 'Section 6. Arrays and Pointers', to quote:

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.

That is the source of the problem. Match up your declaration and definitions. Sorry if this sounds blunt, but I could not help noticing what the linker was telling you...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文