变量只能全局“声明”,而不能修改/(单独初始化)

时间:2019-02-26 13:23:38

标签: c++ c global-variables

免责声明:

  • 这可能是一个非常琐碎的问题(尽管我找不到答案),并且
  • 一个纯粹的理论问题(我从不需要这样做,也从未见过使用这种结构的代码,但我很好奇这种方式是/为什么发生的。)
  • C/C++双重标记,因为我在C和C ++上都测试了一下代码,它只有4行代码(而且唯一的区别是gcc/clang发出了警告而g++/clang++给出了错误。)

背景: 在回复另一个question时,我开始思考为什么OP无法修改一个public static变量。我考虑了一下,然后进一步减少了问题,在这里我可以看到相同的效果,但是不需要任何类或静态成员变量。

问题:然后以下代码可以重现观察结果。

int global_n; // I know it can be initialized right away here also: int global_n = 1;
global_n = 2; // This does not compile in C++. In C it gives a warning about missing type-specifier

int main() {
    global_n = 2; // This does compile in both C/C++ of course
}
  1. 这使我想到了一个问题:全局变量(因此static变量/成员变量)只能在声明它们时直接在那里初始化。但是任何后续修改只能在函数内部进行。正确吗?

  2. 有任何具体原因吗?

3 个答案:

答案 0 :(得分:4)

在函数之外,您不能有语句(即可执行的代码行),只有声明和定义。

global_n = 2;在全局范围内的情况下,C90具有旧版功能,即如果声明的变量没有类型,则其默认类型为int(C99删除了该功能并要求使用类型)。这就是这种情况,这就是为什么您会收到有关缺少类型的警告的原因。

C ++没有该规则,因此它显示为函数之外的语句,这是错误的。

答案 1 :(得分:2)

简单的答案是,该语法不允许在故事结尾的复合语句{...}之外执行代码。

但是,如果深入一点,C也不允许

之类的东西。
// file scope
int x = 0;
int y = x;

C也不允许这样做:

// file scope
int x = func();

这样做的原因是文件范围变量和声明为static的变量都具有静态存储持续时间。实际上,此类变量不是在声明它们的行上初始化的,而是在甚至调用main()之前进行的。 (这也适用于具有静态存储持续时间的C ++对象。)

即使没有看到,也总是在调用main()之前执行启动代码。通常将其称为“ C运行时”或“ CRT”。它的一部分工作是在调​​用main()之前以静态存储持续时间初始化所有变量/对象。

因此,如果您具有以下应用程序代码:

void foo (void)
{
  static int var = 1;
  printf("%d", var);
}

int main (void)
{
  foo();
}

然后在main之前执行的代码将类似于以下简化的伪代码:

void startup (void) // point of entry when executable starts
{
  set memory of "var" to 1
  main();
}

这实际上就是我们可以多次调用foo而无需重新初始化var的原因。 static int var = 1;行实际上并未在放置在源代码中的位置执行,而是更早且只有一次。与局部变量不同,局部变量通常在代码中与声明相同的位置进行初始化。

“ CRT”初始化大致分为三个部分:

  • .data初始化,它将所有静态存储持续时间变量设置为程序员显式初始化为一个值,例如在我的示例中为var
  • .bss初始化将所有变量设置为零,这些变量要么由程序员初始化为零,要么根本没有初始化。
  • 通过其各自的初始化程序初始化具有静态存储持续时间的C ++对象。

除了这种C ++构造,启动时不会调用任何应用程序代码,这就是为什么在C中不允许使用int x = func();之类的代码。

这也是为什么不应使具有静态存储持续时间的C ++对象依赖于彼此的初始化值的原因:源中的声明顺序不一定与“ CRT”中的初始化顺序相对应

答案 2 :(得分:1)

我的答案是关于C ++的。 C可能会不同。

  

全局变量(因此也就是静态变量/成员变量)只能在声明它们后直接在那里初始化。

是的,只能在声明中提供初始化程序。但是,可以在不使用初始化程序的情况下提供声明。示例:

extern int global_n; // only declaration; no definition; no initialiser
int global_n = 42;   // re-declaration; definition; initialiser
  

但是任何后续修改只能在函数内部进行。是吗?

并非完全正确。可以在另一个全局变量的初始化程序中修改全局变量:

int global_n1 = 666;
int global_n2 = global_n1 = 42;

实际上,这可能是一个糟糕的设计选择-至少在此简化示例中;我想那里可以有实际的用例。

  

有什么具体原因吗?

我想您的意思是,为什么只能有声明语句,而函数之外没有其他类型的语句的任何特定原因?

那只是语言的设计选择。 C ++程序是链接在一起的独立单元。语句来自单独的源文件时,应以什么顺序执行?关于静态对象的初始化,它们的执行情况如何?静态初始化的当前状态非常复杂。我认为不允许在命名空间范围内使用表达式语句是一个不错的选择。