为什么c++编译器接受这种初始化方式?static int x = x;

10

我刚刚发现了这件事:

static int x = x;

为什么C++编译器接受这种初始化方式?

我会将其称为编译器异常,但也有可能有人能够给出一个好的解释。

对于静态存储数据而言,使用自身进行变量初始化是可行的... 我已经在VS2015和VS2017编译器以及其他一些在线C++编译器中尝试过这种初始化方式。


你甚至可以使用 static int x = (scanf("%d", &x), x); 这样的语句。 - M.M
2个回答

17

对于static和非static变量而言,实际上是一样的。

如果有初始化,一个名称在其声明符之后立即变得可见。因此在下面的代码中:

static int x = x;

在第一次出现后,名称x将立即变得可见,并且可以在初始化程序中引用。由于它是静态的,因此其初始值是明确定义的(为0)。

即使在块作用域也是合法的:

int x = x;

尽管在这里你可能会收到警告,因为x正在使用其自身的不确定值进行初始化(在大多数情况下,该行为是未定义的)。

这是一件愚蠢的事情,但C++并不会特意阻止你做愚蠢的事情。例如,您可能想声明一个指向自身的指针:

void *p = (void*)&p;

初始化程序在这里引用的是p的地址而不是其值,但是名称p必须可见才能使其工作。添加特殊规则被认为不值得。


2
在C++中,块内的int x = x;是未定义行为:对于除了无符号字符类型之外的不确定值的评估是立即未定义行为,没有像C11+DR451中那样的部分传播不确定值。 - M.M
@M.M 所以,如果它不是未定义的,unsigned char x = x; 做什么? - Daniel H
2
@DanielH 和 unsigned char x; 一样。请参见 [basic.scope.pdecl]。 - M.M
确实存在很酷的用例;也就是说,它的实用性甚至超出了所给出的小例子。不错的答案! - Dúthomhas
2
不需要将其转换void*,只需赋值即可。 - Deduplicator
@Deduplicator 你是对的。我知道在C中转换是隐式完成的;但我对C++不太确定。C允许将void*隐式转换为其他对象指针类型,或从其他对象指针类型隐式转换为void*。C++允许将其他对象指针类型隐式转换为void*,但不允许从void*隐式转换为其他对象指针类型。 - Keith Thompson

0

如果你想禁止C++(以及C)中的所有愚蠢结构,那么你会得到一个相当长的列表。

C++实际上是一种与C非常不同的语言,但它仍然有其根源。而且,至少在开始时,与C兼容的重点非常强调。即使今天,C++标准也包括了C头文件。

因此,许多奇怪的事情可以追溯到C,而C是一种对编译器要求不高但对程序员要求更高的语言。许多结构产生未定义行为或不确定值。这有两个主要原因。首先,它使编写编译器正确并具有低系统要求更容易。这在C的早期非常重要。该语言于1972年发布,比康柏公司发布带有4kB内存的家用电脑VIC-20早8年。其次,它允许编译器生成更快的代码,并使用更少的内存,这在过去非常重要,正如刚才提到的。

因此,即使没有任何有效的用例(我不排除可能会有,尽管我看不到),也没有足够强烈的理由去做些什么。

正如Keith在他的回答中提到的那样,如果变量是静态的,那么它将具有值0,这意味着该值不是不确定的。 static int x = x; 的结果是完全清楚的,那么为什么要禁止它呢?你会让C++规范变得更大、更难以维护,并可能破坏一些旧代码,只是为了解决一个不存在的问题。

当涉及到非静态变量时,情况就不同了,因为该值是不确定的。在这里,有一个更好的理由来禁止这种结构,但另一方面,你已经收到了警告。至少如果你正在使用-Wall,你应该这样做。我还可以补充说,许多警告只是警告而不是错误,只是为了不破坏旧代码。但与其禁止初始化为自身,不如完全禁止使用未初始化的变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接