变量只能全局"declared",但不能修改/(单独初始化)



免责声明:

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

背景:在回答另一个问题时,我开始思考为什么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. 这有什么具体原因吗?

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

在全局范围内global_n = 2;的情况下,C90 具有一个遗留功能,即如果声明变量时没有类型,则默认类型为int(C99 删除了该功能并需要一个类型)。 这就是在这种情况下发生的情况,这也是您收到有关缺少类型的警告的原因。

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

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

但是如果深入挖掘一点,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"中的初始化顺序相对应。

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

相关内容

  • 没有找到相关文章

最新更新