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

Ami*_*mar 27 c++ declaration definition extern

这段代码很好编译:

extern int i = 10;

void test()
{
    std::cout << "Hi" << i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

虽然这个错误:

void test()
{
    extern int i = 10;
    std::cout << "Hi" << i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

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

我读了C++ Primer

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

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

Chr*_*sCM 19

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

当您声明符号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();
}
Run Code Online (Sandbox Code Playgroud)

这个例子应该输出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();
}
Run Code Online (Sandbox Code Playgroud)

test2的输出应该是0还是1000?另外看看我的test3,这里我们简洁地说,将我的i链接到外部定义的i,并将其值分配为1000.这与尝试"初始化"值非常不同.

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

  • 我认为他的意思是删除`extern`关键字. (3认同)
  • 在你的第一个程序中,即使从函数中删除 extern 之后它也会给我 Hi11 (2认同)

Mik*_*our 8

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

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

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


yua*_*uan 5

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

当一个名称可能表示与另一个作​​用域中的声明引入的名称相同的对象、引用、函数、类型、模板、命名空间或值时,则称该名称 具有链接:

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

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

在块作用域中声明的函数名称和由块作用域 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
Run Code Online (Sandbox Code Playgroud)

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

     extern int i =10;
Run Code Online (Sandbox Code Playgroud)

     extern int i;//include in .hpp is recommended 
     int i =10;//global or namespace variable defination
Run Code Online (Sandbox Code Playgroud)