下面的C程序可以很好地编译(g 5.4在使用-Wall调用时至少会给出一个警告):
-Wall
int main(int argc, char *argv[]) { int i = i; // ! return 0; }
字符串即使是像
int& p = p;
型被编译器吞掉了。现在我的问题是:为什么这样的初始化是法律的?有任何实际的用例吗?或者它只是语言的一般设计的结果?
6ovsh4lw1#
这是一个规则的副作用,一个名字在声明后立即在作用域中。没有必要为了防止编写明显无意义的代码而将这个简单的规则复杂化。
o75abkj42#
仅仅因为编译器接受它(语法上有效的代码)并不意味着它有定义良好的行为。编译器并不需要诊断所有的未定义行为或其他类型的问题。该标准允许它自由地接受和翻译错误的代码,假设如果结果是未定义的或无意义的,程序员就不会编写这些代码。所以,编译器没有警告或错误并不能 * 以任何方式 * 证明你的程序有良好的行为定义。遵循语言规则是 * 你的 * 责任。编译器通常试图通过指出明显的缺陷来帮助你,但最终还是要靠 * 你 * 来确保你的程序有意义。像int i = i;这样的东西没有意义,但在语法上是正确的,所以编译器可能会警告你,也可能不会警告你,但在任何情况下,它都有权生成垃圾(而不是告诉你),因为你违反了规则,调用了Undefined Behavior。
int i = i;
gupuwyp23#
我猜你的问题的要点是关于为什么第二个标识符被识别为与第一个标识符标识相同的对象,在int i = i;或int &p = p;中这在C++14标准的[basic.scope.pdecl]/1中定义:名称的声明点紧接在其完整的声明符之后和初始化器(如果有的话)之前,除非下面有说明。[示例:
int &p = p;
unsigned char x = 12; { unsigned char x = x; }
字符串在这里,第二个x用它自己的(不确定的)值初始化。这些语句的语义由其他线程覆盖:
x
int x = x;
注意-引用的例子在语义上与int i = i;不同,因为它不是UB来计算未初始化的unsigned char,而是UB来计算未初始化的int。如链接线程中所述,g和clang在检测到这种情况时会给出给予警告。关于作用域规则的基本原理:我不确定,但作用域规则在C中存在,所以可能它只是进入了C,现在改变它会很混乱。如果我们说声明的变量不在其初始化器的作用域中,那么int i = i;可能会使第二个i从外部作用域中找到i,这也会引起混淆。
unsigned char
int
i
3条答案
按热度按时间6ovsh4lw1#
这是一个规则的副作用,一个名字在声明后立即在作用域中。没有必要为了防止编写明显无意义的代码而将这个简单的规则复杂化。
o75abkj42#
仅仅因为编译器接受它(语法上有效的代码)并不意味着它有定义良好的行为。
编译器并不需要诊断所有的未定义行为或其他类型的问题。该标准允许它自由地接受和翻译错误的代码,假设如果结果是未定义的或无意义的,程序员就不会编写这些代码。
所以,编译器没有警告或错误并不能 * 以任何方式 * 证明你的程序有良好的行为定义。遵循语言规则是 * 你的 * 责任。编译器通常试图通过指出明显的缺陷来帮助你,但最终还是要靠 * 你 * 来确保你的程序有意义。
像
int i = i;
这样的东西没有意义,但在语法上是正确的,所以编译器可能会警告你,也可能不会警告你,但在任何情况下,它都有权生成垃圾(而不是告诉你),因为你违反了规则,调用了Undefined Behavior。gupuwyp23#
我猜你的问题的要点是关于为什么第二个标识符被识别为与第一个标识符标识相同的对象,在
int i = i;
或int &p = p;
中这在C++14标准的[basic.scope.pdecl]/1中定义:
名称的声明点紧接在其完整的声明符之后和初始化器(如果有的话)之前,除非下面有说明。[示例:
字符串
在这里,第二个
x
用它自己的(不确定的)值初始化。这些语句的语义由其他线程覆盖:
int x = x;
UB?注意-引用的例子在语义上与
int i = i;
不同,因为它不是UB来计算未初始化的unsigned char
,而是UB来计算未初始化的int
。如链接线程中所述,g和clang在检测到这种情况时会给出给予警告。
关于作用域规则的基本原理:我不确定,但作用域规则在C中存在,所以可能它只是进入了C,现在改变它会很混乱。
如果我们说声明的变量不在其初始化器的作用域中,那么
int i = i;
可能会使第二个i
从外部作用域中找到i
,这也会引起混淆。