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

问题描述 投票:24回答:5

这段代码很好编译:

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上提供初始化程序是错误的。

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

c++ declaration definition extern
5个回答
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.这与尝试“初始化”值非常不同。

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


7
投票

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

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

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


4
投票

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

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

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

0
投票

最简单的方法:

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

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


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

© www.soinside.com 2019 - 2024. All rights reserved.