免责声明:
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
}
这使我想到了一个问题:全局变量(因此static
变量/成员变量)只能在声明它们时直接在那里初始化。但是任何后续修改只能在函数内部进行。正确吗?
有任何具体原因吗?
答案 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中不允许使用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 ++程序是链接在一起的独立单元。语句来自单独的源文件时,应以什么顺序执行?关于静态对象的初始化,它们的执行情况如何?静态初始化的当前状态非常复杂。我认为不允许在命名空间范围内使用表达式语句是一个不错的选择。