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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么在函数内初始化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是一个定义。在函数内部的extern上提供初始化程序是错误的。

有人可以提供一个解释,说明为什么这是一个错误,如果在函数本地完成,而在全局范围允许相同?

答案

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

当您声明符号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.这与尝试“初始化”值非常不同。

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

另一答案

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

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

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

另一答案

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

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

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

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

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

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

A.kh.t 0.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
另一答案

最简单的方法:

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

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

另一答案

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变量。

以上是关于为什么在函数内初始化extern变量会产生错误?的主要内容,如果未能解决你的问题,请参考以下文章

在类内初始化数组会产生 std::bad_alloc 错误,但在外面不会?

static extern volatile

变量存储类型(auto static extern)

extern关键字用法总结(顺带初始化和赋值的区别)

C++中如何定义变量

关于存储持续性,作用域,链接性,static与extern