两个不同的.cpp文件中的C/ c++作用域

C/C++ Scope in two different .cpp files

本文关键字:文件 作用域 c++ cpp 两个      更新时间:2023-10-16

我想知道为什么你不能在2个不同的。cpp文件中声明一个具有相同名称的全局。我的理解是考虑范围,它应该只对特定的。cpp文件可见,而不是其他地方,但它显然是抱怨。我这样做的原因是为了代码的通用性,仅此而已。什么好主意吗?

编辑清晰

a.cpp

int g_x;

b.cpp

int g_x;

要使全局变量(或函数)仅对声明它的文件可见,

  1. 声明为static,或
  2. (c++中首选的方式)将变量放在一个无名的命名空间中。这似乎会使它从名称空间外部无法访问,但它实际上做的是使它只有对它所在的文件可见。

访问在其他文件中声明的全局变量(不是static或在匿名命名空间中),使用extern

原因与不能在两个不同的文件中使用同名函数的原因相同。它混淆了链接器,因为默认情况下全局变量具有外部链接。static或处于匿名名称空间中使它们具有内部链接,这使它们类似于"局部全局变量"。

问题:
全局变量具有外部链接,即:它们在整个程序中是可见的。如果两个不同的文件有相同的符号名变量,那么它就打破了ODR(一个定义规则)。

单一定义规则(ODR),顾名思义,要求具有外部链接的对象、非内联函数、类、枚举或模板必须在程序中只有一个定义。


解决方案:
文件中的全局变量应该具有内部链接,以避免违反ODR。有两种方法可以实现这一点:

让你的全局变量在文件static
使用匿名/未命名命名空间


好读:
匿名的/未命名的命名空间
语言标准:统一定义规则


标准风扇:
c++ 11 3.2一个定义规则[basic.def.odr]:

任何翻译单元不得包含任何变量、函数、类类型、枚举类型或模板的一个以上定义。

第三段:

每个程序应该只包含一个在该程序中使用的非内联函数或变量的定义;不需要诊断。定义可以显式出现在程序中,也可以在标准库或用户定义库中找到,或者(适当时)隐式定义(参见12.1、12.4和12.8)。内联函数应在每一个使用该函数的翻译单元中定义。

c++ 11 7.3.1.1未命名命名空间[namspace . Unnamed]

1/未命名的命名空间定义的行为就像被

取代一样
inlineoptnamespace unique { /* empty body */ }    
using namespace unique ;      
namespace unique { namespace-body }       

当且仅当inline出现在未命名的命名空间定义中时,翻译单元中出现的所有unique都被相同的标识符替换,并且该标识符与整个程序中的所有其他标识符不同。94

注意: c++ 03已弃用static在命名空间范围内声明对象,但在c++ 11中已删除此弃用。

得到链接器错误的原因是变量具有外部链接,并且位于相同的命名空间(即全局命名空间)。

通常和推荐的补救措施是将它们放在单独的命名空间中。

您可以通过使用匿名命名空间来完成此操作,如下所示:

namespace {
    int my_global_variable;
}

这个命名空间在每个翻译单元中都有一个唯一的名称。例如,在一个翻译单元中,自动名称可能是"gorblegorble123"。这样就像编译器在后面放置了以下代码一样:

using namespace gorblegorble123;    // As if this is present.

其他翻译单元同上,但有不同的唯一名称。

这是最通用的技术。它特别适用于类定义。


正如在其他一些答案中提到的,您可以或者使用static指定内部链接,该变量对链接器不可见。

在c++ 98和c++ 03中,对于命名空间作用域内的对象不支持这种用法,但在c++ 11中已取消了这种用法。

static是一个有限的特殊情况符号,可以应用于对象和函数,但不能应用于类。

声明变量static (C和c++):

static int myGlobalVariable;

或在匿名命名空间中声明(仅限c++):

namespace {
    int myGlobalVariable;
}

why you can not declare a global with the same name in 2 different .cpp files

因为任何对一个实体有多个定义的程序都是病态的。在您的例子中,您有两个命名相同的实体,因此程序是病态的。

解决这个问题的首选方法(至少在c++ 03中)是使用未命名的名称空间:

file1.cpp

namespace
{
  int n;
};

file2.cpp

namespace
{
  int n;
};

将声明放置在未命名的命名空间中,使该实体仅在该翻译单元中可见。

既然这个问题也被标记为'c',我想加上我的5美分。在C语言中没有"单一定义规则"。

在C (C99及之前版本,不确定C1x)中,具有文件作用域且没有存储类说明符的对象构成暂定定义。(部分6.9.2)。

在第144页的示例1中,列出了几个示例,全部集中在一个文件中的定义/声明。然而,一个extern声明和一个没有存储类说明符的声明(i3)可以很好地结合在一起,构成一个带有外部链接的定义。

这意味着可能存在第二个文件引用该定义,或者使用extern,或者同样没有存储类说明符。

在C语言中,你可能有两个文件它们都有暂定的定义,比如

int i3;

和两者都将引用一个类型为int的变量,并且链接器将确保两者都引用相同的内存位置。即使int abc;char abc在两个不同的文件中,常规链接器也不会产生错误;希望它能提醒你。

H&S5有一些关于各种类型的链接器如何处理这种情况的更多细节,但使用公共内存模型,上述内容得到确认。至少在我看来是这样。

更多参考,请看这里,特别是Alok的答案