c++ - 为什么我在头文件中定义 `const int`时没有重复定义错误?

标签 c++ compilation linker extern

如果之前有人问过这个问题,我们深表歉意。我在网上查了一下,没有找到答案。

假设我有一个文件 Common.h,并且 A.cppB.cpp 包含 Common.h.

如果我想在 Common 翻译单元中拥有一个全局 const char *,我必须在 Common 中将其设置为 extern .h,并在Common.cpp中定义它。否则,如果我只是在 Common.h 中定义 const char * MSG = "Hello World";,我会在编译期间收到 duplicate symbol 错误.

但是,如果我只是在 Common.h 中使用 const int CONSTANT = 10; 之类的语句定义一个全局 const int,那么代码编译时不会出现重复符号错误,一切正常。

我很困惑为什么会出现这种情况。在我看来,上面两个例子之间的唯一区别是类型,我认为这不应该有什么区别。为什么 C 字符串会出现重复符号错误,而整数却不会?

假设 main.cppA.hB.hA.cppB。 cpp 如下所示:

// A.h
#pragma once
void f();
// A.cpp
#include "A.h"
#include "Common.h"

#include <iostream>

void f() {
    std::cout << MSG << std::endl;
}
// B.h
#pragma once
void g();
// B.cpp
#include "B.h"
#include "Common.h"

#include <iostream>

void g() {
    std::cout << CONSTANT << std::endl;
}
// main.cpp
#include "A.h"
#include "B.h"

int main()
{
    f();
    g();
}

现在,假设我们使用命令 g++ main.cpp A.cpp B.cpp Common.cpp -std=c++14 进行编译。

如果我们将 Common.hCommon.cpp 设为以下内容,则编译会失败并出现错误 duplicate symbol MSG:

// Common.h
#pragma once
const char * MSG = "Hello World";
const int CONSTANT = 10; // defined in header file
// Common.cpp
// empty

但是,这可以编译:

// Common.h
#pragma once
extern const char * MSG;
const int CONSTANT = 10; // defined in header file
// Common.cpp
#include "Common.h"
const char * MSG = "Hello World";

我想知道为什么我们需要 extern 并分隔字符串的定义和声明,而不是 int。

有人建议将 C 字符串类型设置为 const char * const 而不是 const char *。为什么使指针常量有效?另外,在这种情况下,这个解决方案和我上面提供的解决方案(我们用字符串 extern 代替并分割定义/声明)有什么区别?为什么这两种方法都能解决编译错误,方法之间有什么区别?

我还注意到,如果我将 const int 转换为 int,那么我会再次收到 duplicate symbol 错误。我觉得这背后的原因与我上面问题的答案有关。为什么会这样?

最佳答案

这是C和C++的区别之一。

在 C++ 中,const 变量是隐式静态的,即仅对当前翻译单元可见。在 C 中,它是隐式 extern 的,因此对整个程序可见(这也是 C 和 C++ 中其他非常量声明的默认设置)。

这解释了你的观察结果。

注意:变量的 const char *p 声明不是 const 变量。这意味着它指向一个const变量(即*p无法修改),但p本身不是const.所以这里的行为是不同的。 const char * const p 将是一个 const 声明。

关于c++ - 为什么我在头文件中定义 `const int`时没有重复定义错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63840677/

相关文章:

c++ - 删除 Windows 库依赖项

c++ - C++中的单独编译

linux - Yocto 多文件编译

c# - .Net接口(interface)继承编译

c++ - 将自定义对象文件与 Cabal 链接

iphone - : libtool: -dynamic not specified the following flags are invalid: -ObjC 是什么意思

c++ - 尝试构建 UETorch 时 Xcode 8.1 中的链接器错误

c++ - 如何使用父目录中的文件进行命令行编译?

检测到 C++ 堆损坏

c++ - 快速 64 位比较