16

我见过人们使用 2 种方法来声明和定义char *.

方法1:头文件有以下内容

extern const char* COUNTRY_NAME_USA = "USA";

方法2:
头文件有以下声明:

extern const char* COUNTRY_NAME_USA;

cpp 文件具有以下定义:

extern const char* COUNTRY_NAME_USA = "USA";
  1. 方法1在某种程度上是错误的吗?
  2. 两者有什么区别 ?
  3. const char * const var我理解 " " 和 " "之间的区别const char * var。如果在上述方法中,如果const char * const var像方法 1 一样在标头中声明和定义“”,是否有意义?
4

3 回答 3

33

第一种方法确实是错误的,因为它在头文件中定义COUNTRY_NAME_USA了具有外部链接的对象。一旦该头文件包含在多个翻译单元中,就会违反一个定义规则 (ODR)。代码将无法编译(更准确地说,它将无法链接)。

第二种方法是正确的。关键字extern在定义中是可选的,即在 cpp 文件中你可以这样做

const char* COUNTRY_NAME_USA = "USA"

假设头文件中的声明在此翻译单元中的此定义之前。

另外,我猜由于对象名称是大写的,它可能是一个常量。如果是这样,那么它应该被声明/定义为const char* const COUNTRY_NAME_USA(注意额外的const)。

最后,考虑到最后一个细节,您可以将常量定义为

const char* const COUNTRY_NAME_USA = "USA"; // no `extern`!

在头文件中。由于它现在是一个常量,因此默认情况下它具有内部链接,这意味着即使将头文件包含在多个翻译单元中也不会违反 ODR。在这种情况下,您会在每个翻译单元中获得一个单独的COUNTRY_NAME_USA左值(而在extern方法中,您会为整个程序获得一个左值)。只有你知道你需要什么。

于 2010-05-21T05:59:32.520 回答
10

重点是什么?

如果您想查找字符串(可以本地化),这将是最好的:

namespace CountryNames {
    const char* const US = "USA";
};

由于指针是 const,它现在具有内部链接并且不会导致多个定义。大多数链接器还将结合冗余常量,因此您不会浪费可执行文件中的空间。

但是,如果您想通过指针相等来比较字符串,则上述内容不可移植,因为只有在链接器执行常量折叠优化时,指针才会相等。在这种情况下,在头文件中声明一个外部指针是要走的路(如果你不打算重新定位它,它应该再次是 const )。

于 2010-05-21T04:38:25.297 回答
5

如果您必须有全局变量,通常的做法是在 .h 文件中声明它们并在一个(并且只有一个).cpp 文件中定义它们。

在 .h 文件中;

extern int x;

在 .cpp 文件中;

int x=3;

我使用了 int (也许是最基本的基本类型?)而不是 const char * ,因为您的问题的本质不取决于变量的类型。

基本思想是您可以多次声明一个变量,因此每个包含 .h 文件的 .cpp 文件都声明该变量,这很好。但是你只定义一次。定义是您为变量分配初始值的语句(使用 =)。您不希望在 .h 文件中定义,因为如果 .h 文件包含在多个 .cpp 文件中,您将获得多个定义。如果您对一个变量有多个定义,那么在链接时就会出现问题,因为链接器想要分配变量的地址,并且如果它有多个副本,则无法合理地做到这一点。

稍后添加的附加信息试图缓解 Sud 的困惑;

尝试将您的问题减少到最小的部分以更好地理解它;

假设您有一个包含三个 .cpp 文件的程序。为了构建程序,每个 .cpp 都被单独编译以创建三个目标文件,然后将三个目标文件链接在一起。如果三个.cpp文件如下(示例A,组织好);

文件1.cpp

extern int x;

文件2.cpp

extern int x;

文件 3.cpp

extern int x;

然后文件将毫无问题地编译并链接在一起(至少就变量 x 而言)。没有问题,因为每个文件只声明变量 x。声明只是说明在某处我可能(或可能不会)使用的变量。

以下是实现相同目标的更好方法(示例 A,更好的组织);

头文件.h

extern int x;

文件1.cpp

#include "header.h"

文件2.cpp

#include "header.h"

文件 3.cpp

#include "header.h"

这实际上是完全相同的,对于三个编译中的每一个,编译器在处理 .cpp 文件(或专家称之为翻译单元)时看到的文本都相同,因为 #include 指令只是从另一个文件中提取文本. 尽管如此,这是对前面示例的改进,因为我们只将声明放在一个文件中,而不是在多个文件中。

现在考虑另一个工作示例(示例 B,良好的组织);

文件1.cpp

extern int x;

文件2.cpp

extern int x;

文件 3.cpp

extern int x;
int x=3;

这也可以正常工作。所有三个 .cpp 文件都声明了 x 并且一个实际定义了它。我们可以继续在操作变量 x 的三个文件中的任何一个中的函数中添加更多代码,我们不会得到任何错误。同样,我们应该使用一个头文件,以便声明只进入一个物理文件(示例 B,更好的组织)。

头文件.h

extern int x;

文件1.cpp

#include "header.h"

文件2.cpp

#include "header.h"

文件 3.cpp

#include "header.h"
int x=3;

最后考虑一个不起作用的例子(例子C,不起作用);

文件1.cpp

int x=3;

文件2.cpp

int x=3;

文件 3.cpp

int x=3;

每个文件都可以毫无问题地编译。问题发生在链接时,因为现在我们已经定义了三个单独的 int x 变量。它们具有相同的名称并且都是全局可见的。链接器的工作是将单个程序所需的所有对象提取到一个可执行文件中。全局可见的对象必须具有唯一的名称,以便链接器可以将对象的单个副本放在可执行文件中一个定义的地址(位置),并允许所有其他对象在该地址访问它。在这种情况下,链接器无法使用全局变量 x 完成它的工作,因此会扼杀一个错误。

顺便说一句,给出不同的定义不同的初始值并不能解决问题。在每个定义之前加上关键字 static 确实解决了这个问题,因为现在变量不是全局可见的,而是在定义的 .cpp 文件中可见。

如果将全局变量定义放入头文件中,则基本没有任何变化(例如 C,头文件组织在这种情况下没有帮助);

头文件.h

int x=3;  // Don't put this in a .h file, causes multiple definition link error

文件1.cpp

#include "header.h"

文件2.cpp

#include "header.h"

文件 3.cpp

#include "header.h"

唷,我希望有人读到这篇文章并从中受益。有时提问者急需一个简单的基本概念解释,而不是高级计算机科学家的解释。

于 2010-05-21T05:08:15.750 回答