31

这段代码编译得很好:

extern int i = 10;

void test()
{
    std::cout << "Hi" << i << std::endl;
}

虽然此代码给出了错误:

void test()
{
    extern int i = 10;
    std::cout << "Hi" << i << std::endl;
}

错误:“i”同时具有“extern”和初始化程序

我在C++ Primer中读到了这个:

任何包含显式初始值设定项的声明都是定义。我们可以为定义为 extern 的变量提供初始化程序,但这样做会覆盖 extern。具有初始化程序的 extern 是一个定义。在函数内部的外部提供初始化程序是错误的

有人可以解释为什么如果在函数中本地完成这是一个错误,而在全局范围内允许这样做?

4

5 回答 5

25

在函数内部定义外部变量没有意义的原因如下:

当您声明一个符号 extern 时,您是在告诉编译器将此值的所有此类出现链接到同一个符号。extern int i 的任何出现;在您的程序中将链接到外部定义的 i。看这个例子:

#include <iostream>
using namespace std;

extern int i;
int i = 10;
void test()
{
    std::cout << "Hi" << i << std::endl;
}

int main()
{
    extern int i;
    i++;
    test();
}

此示例应输出 hi11。但是,如果我们删除 main 内部的 extern,它将输出 10。这是因为没有 extern,i 不会链接到全局 i,而是创建它自己的 i 的本地副本。

在函数内部定义 extern i 的原因是没有意义的,如果我们允许任何函数“定义” i 会怎样。哪个函数先运行?什么时候定义?

假设以下示例有效,输出将是什么???

#include <iostream>
using namespace std;

extern int i;
int i = 10;
void test()
{
    std::cout << "Hi" << i << std::endl;
}

void test2() {
    extern int i = 1000;
    std::cout<< "HI" << i << std::endl;
}

void test3() {
    extern int i;
    i = 1000;
    std::cout<< "HI" << i << std::endl;
}

int main()
{
    extern int i;
    i++;
    test();
    i = 0;
    test2();
}

test2 的输出应该是 0 还是 1000?再看看我的 test3,这里我们简明扼要地说,将我的 i 链接到外部定义的 i,并将其值指定为 1000。这与尝试“初始化”一个值非常不同。

简而言之,外部变量实际上只对全局变量有意义,并且应该在全局范围内定义。在您的示例中,第一个版本也无法为我编译。我觉得这很有趣。可能值得查看标准文档以查看是否定义简洁,或者您的编译器是否可能以旨在添加额外保护的方式处理此问题......

于 2013-06-13T15:16:31.433 回答
11

通过在声明中添加初始化器,它成为全局变量的定义。它相当于没有 的相同定义extern,这就是你的书说它“覆盖外部”时的意思。

虽然可以在函数内声明(使用)全局变量extern,但它们不能在那里定义,只能在命名空间范围内定义。这就是为什么第二个片段是错误的。

如果你想知道为什么 C 的设计者(这些规则从何而来 C++)选择在这里允许声明而不是定义,那么恐怕我对语言的历史了解得不够详细,无法回答。

于 2013-06-13T15:19:23.910 回答
4

首先,您应该熟悉联动的概念和外部联动的含义:

当一个名称可能表示与另一个范围内的声明引入的名称相同的对象、引用、函数、类型、模板、命名空间或值时, 就称其具有链接:

当一个名称具有外部链接时,它所表示的实体可以被其他翻译单元的范围或同一翻译单元的其他范围的名称引用。
--3.5.6.2 n3242

其功能static不同于externextern只是一个请求,static是一个命令。

在块作用域中声明的函数名和由块作用域外部声明声明的变量名具有链接。

  • 如果存在具有相同名称和类型的链接的实体的可见声明,忽略在最内层封闭命名空间范围之外声明的实体,块范围声明声明相同的实体并接收前一个声明的链接。
  • 如果存在不止一个这样的匹配实体,则程序是非良构的。
  • 否则,如果没有找到匹配的实体,则块范围实体接收外部链接。

--3.5.6.6 n3242

因此,在块范围内,建议执行以下过程:

     extern int i;//declare it,request the linkage according to 3.5.6.6 above
     i = 10;//modify it when has link to a defination

对于全局 extern 声明可能是转换形式

     extern int i =10;

     extern int i;//include in .hpp is recommended 
     int i =10;//global or namespace variable defination
于 2013-06-14T01:34:53.090 回答
0

最简单的表达方式:

关键字的目的extern是声明一个对象而不定义它。通过定义它,您基本上是在告诉编译器“不要赋值而是赋值”。那没有意义——它永远不应该在函数内部外部完成。大多数编译器要么警告你并继续进行,要么根本不编译并给出错误。

尽管详细解释此问题的范围超出了此问题的范围,但您可能会发现阅读 此问题extern的答案很有用。

于 2015-05-23T01:20:56.357 回答
0

extern在任何函数运行之前初始化变量: en.cppreference.com/w/cpp/language/initialization#Non-local_variables

如果它被声明static而不是extern在函数块中,它仍然具有静态存储持续时间,但它的链接将是该函数与外部的本地链接。因此,当执行第一次通过函数内的该行运行时,它将被初始化: en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

所以可以static在功能块中初始化变量,但不能在extern那里初始化变量。

于 2017-05-07T19:10:49.020 回答