为什么在函数内初始化extern变量会产生错误?

时间:2013-06-13 14:57:45

标签: c++ declaration definition extern

此代码编译良好:

extern int i = 10;

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

虽然此代码出错:

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

错误:'我'同时拥有'extern'和初始化程序

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

  

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

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

5 个答案:

答案 0 :(得分:18)

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

当您声明符号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。 HOwever,如果我们删除主内部的extern,它将输出10.这是因为没有extern,我没有链接到全局i,但创建它自己的本地副本i。

如果我们允许任何函数“定义”i,那么在函数内定义extern 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.这与尝试“初始化”值非常不同。

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

答案 1 :(得分:7)

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

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

如果你想知道为什么C的设计者(这些规则来自C ++)选择允许声明而不是这里的定义,那么我恐怕我不太清楚地知道语言的历史。< / p>

答案 2 :(得分:4)

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

  

当一个名称可能表示同一个对象时,该名称被认为具有链接,   引用,函数,类型,模板,命名空间或值作为名称   由另一个范围内的声明引入:

     

当名称具有外部链接时,它表示的实体可以是   来自其他翻译单位范围的名称或来自   同一翻译单位的其他范围。
                  - 3.5.6.2 n3242

static的功能与extern不同,extern只是一个请求,static是一个命令。

  

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

     
      
  • 如果存在具有相同名称和类型的链接的实体的可见声明,忽略在最内部封闭命名空间范围之外声明的实体,则块范围声明声明该实体并接收先前声明的链接。
  •   
  • 如果有多个此类匹配实体,则该程序格式不正确。
  •   
  • 否则,如果未找到匹配的实体,则块范围实体将接收外部链接。
  •   
     

- 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

答案 3 :(得分:0)

最简单的说法:

extern 关键字的目的是声明一个对象而不定义它。通过定义它,您基本上可以告诉编译器&#34;不要分配值但是分配值&#34;。这没有意义 - 在函数外部的内部永远不应该这样做。大多数编译器要么警告你,要么继续进行,否则他们根本不会编译并给出错误。

虽然详细解释 extern 的内容超出了此问题的范围,但您可能会发现阅读答案很有用this question

答案 4 :(得分:0)

在任何函数运行之前初始化

extern变量: en.cppreference.com/w/cpp/language/initialization#Non-local_variables

如果在功能块中声明static而不是extern,它仍然会有静态存储持续时间,但它的&#39; 链接本地外部相对应。因此,当执行首次遍历函数中的该行时,它将被初始化: en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

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