我刚刚发现了这件事:
static int x = x;
为什么C++编译器接受这种初始化方式?
我会将其称为编译器异常,但也有可能有人能够给出一个好的解释。
对于静态存储数据而言,使用自身进行变量初始化是可行的... 我已经在VS2015和VS2017编译器以及其他一些在线C++编译器中尝试过这种初始化方式。
我刚刚发现了这件事:
static int x = x;
为什么C++编译器接受这种初始化方式?
我会将其称为编译器异常,但也有可能有人能够给出一个好的解释。
对于静态存储数据而言,使用自身进行变量初始化是可行的... 我已经在VS2015和VS2017编译器以及其他一些在线C++编译器中尝试过这种初始化方式。
对于static
和非static
变量而言,实际上是一样的。
如果有初始化,一个名称在其声明符之后立即变得可见。因此在下面的代码中:
static int x = x;
在第一次出现后,名称x
将立即变得可见,并且可以在初始化程序中引用。由于它是静态的,因此其初始值是明确定义的(为0
)。
即使在块作用域也是合法的:
int x = x;
尽管在这里你可能会收到警告,因为x
正在使用其自身的不确定值进行初始化(在大多数情况下,该行为是未定义的)。
这是一件愚蠢的事情,但C++并不会特意阻止你做愚蠢的事情。例如,您可能想声明一个指向自身的指针:
void *p = (void*)&p;
初始化程序在这里引用的是p
的地址而不是其值,但是名称p
必须可见才能使其工作。添加特殊规则被认为不值得。
int x = x;
是未定义行为:对于除了无符号字符类型之外的不确定值的评估是立即未定义行为,没有像C11+DR451中那样的部分传播不确定值。 - M.Munsigned char x = x;
做什么? - Daniel Hunsigned char x;
一样。请参见 [basic.scope.pdecl]。 - M.Mvoid*
,只需赋值即可。 - Deduplicatorvoid*
隐式转换为其他对象指针类型,或从其他对象指针类型隐式转换为void*
。C++允许将其他对象指针类型隐式转换为void*
,但不允许从void*
隐式转换为其他对象指针类型。 - Keith Thompson如果你想禁止C++(以及C)中的所有愚蠢结构,那么你会得到一个相当长的列表。
C++实际上是一种与C非常不同的语言,但它仍然有其根源。而且,至少在开始时,与C兼容的重点非常强调。即使今天,C++标准也包括了C头文件。
因此,许多奇怪的事情可以追溯到C,而C是一种对编译器要求不高但对程序员要求更高的语言。许多结构产生未定义行为或不确定值。这有两个主要原因。首先,它使编写编译器正确并具有低系统要求更容易。这在C的早期非常重要。该语言于1972年发布,比康柏公司发布带有4kB内存的家用电脑VIC-20早8年。其次,它允许编译器生成更快的代码,并使用更少的内存,这在过去非常重要,正如刚才提到的。
因此,即使没有任何有效的用例(我不排除可能会有,尽管我看不到),也没有足够强烈的理由去做些什么。
正如Keith在他的回答中提到的那样,如果变量是静态的,那么它将具有值0,这意味着该值不是不确定的。 static int x = x;
的结果是完全清楚的,那么为什么要禁止它呢?你会让C++规范变得更大、更难以维护,并可能破坏一些旧代码,只是为了解决一个不存在的问题。
当涉及到非静态变量时,情况就不同了,因为该值是不确定的。在这里,有一个更好的理由来禁止这种结构,但另一方面,你已经收到了警告。至少如果你正在使用-Wall
,你应该这样做。我还可以补充说,许多警告只是警告而不是错误,只是为了不破坏旧代码。但与其禁止初始化为自身,不如完全禁止使用未初始化的变量。
static int x = (scanf("%d", &x), x);
这样的语句。 - M.M