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

问题描述 投票:3回答:3

免责声明:

  • 这可能是一个非常微不足道的问题(虽然我找不到答案),并且
  • 一个纯粹的理论问题(我从来不需要这样做,也从未见过使用过这种结构的代码,但我只是好奇它是如何/为什么会这样发生的。)
  • 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. 有什么具体的原因吗?
c++ c global-variables
3个回答
4
投票

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

对于global_n = 2;在全局范围内的情况,C90具有遗留特征,如果声明变量没有类型,则具有默认类型int(C99删除了该特征并且需要类型)。这就是在这种情况下发生的事情,这也是你得到关于缺失类型的警告的原因。

C ++没有该规则,因此它在函数外部显示为错误。


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”中的初始化顺序相对应。


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 ++程序是链接在一起的独立单元。当语句来自不同的源文件时,它们应以什么顺序执行?它们在初始化静态对象方面的执行情况如何?静态初始化的当前状态足够复杂;我认为不允许在命名空间范围内使用表达式语句是一个不错的选择。

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