今天,当我与我的编译器发生另一项国内事务时,这个问题突然出现在我的脑海中。尽管我的粉红色(由于我在工作时所做的所有分号按压),我在if
声明之前设法错过了一个。显然,这导致编译错误:
错误C2143:语法错误:缺少';'在'if'之前
所以我想知道“好吧,为什么你不能告诉我在问题之后缺少分号而不是行的行。”然后我开始尝试其他类似的语法错误:
错误C2065:'myUndeclared':未声明的标识符
错误C2143:语法错误:在'if'
之前缺少')'等...
现在,所有这些错误同样会在问题之后将我带到行并在if
语句之前抱怨某事。
请考虑以下事项:
SomeFunction(x) //Notice, there is no ';' here
if(bSomeCondition)
{
...
}
我遇到两个编译错误:
(第265行)错误C2065:'x':未声明的标识符
(第266行)错误C2143:语法错误:缺少';'在'if'之前
然而,第一个错误正确告诉我行号,尽管缺少分号。这告诉我编译器在解析时不会被绊倒,并且能够使它超过分号问题。那么,为什么编译器坚持以这种方式报告语法错误呢?在找到它们的行上报告其他错误(非语法)。这是否与编译器进行多次传递有关?基本上,我希望具有C ++编译器工作知识的人可以具体解释编译器正在做什么,这需要以“之前”的方式报告错误。
答案 0 :(得分:22)
对于“为什么C / C ++错误消息很糟糕”这个更普遍的问题的简短回答是“有时候C ++很难解析”(它实际上没有上下文无关语法)。但是,这不是一个真正有效的原因 - 人们仍然可以制作比大多数C ++编译器记录更好的诊断信息的工具。
更实际的答案是“编译器作者继承了遗留代码库,这些代码库没有重视错误消息”,加上温和的“编译器作者懒惰”,以及“诊断报告不是一个令人兴奋的问题”。大多数编译器编写者会添加新的语言功能或3%的codegen性能改进,而不是在代码库上进行重大的重构以允许正确的错误报告。关于“为什么错误没有正确地定位到'导致'他们”的行的具体问题就是这样的一个例子。编译器通常无法解决;
缺失的技术原因
,然后告诉你最后;
缺少语句的源跨度 - 即使存在C ++的一般空白不变性。只是存储这些信息(很大程度上)在历史上被忽略了。
尽管如此,新编译器并没有因数十年的旧代码而受到阻碍。看看Clang compiler,它以合理的错误消息为荣。 page on diagnostics显示它们比GCC好多少。这种情况的一个例子是:
$ gcc-4.2 t.c
t.c: In function 'foo':
t.c:5: error: expected ';' before '}' token
$ clang t.c
t.c:4:8: error: expected ';' after expression
bar()
^
;
或者更令人印象深刻的是:
$ cat t.cc
template<class T>
class a {}
class temp {};
a<temp> b;
struct b {
}
$ gcc-4.2 t.cc
t.cc:3: error: multiple types in one declaration
t.cc:4: error: non-template type 'a' used as a template
t.cc:4: error: invalid type in declaration before ';' token
t.cc:6: error: expected unqualified-id at end of input
$ clang t.cc
t.cc:2:11: error: expected ';' after class
class a {}
^
;
t.cc:6:2: error: expected ';' after struct
}
^
;
看,它甚至告诉我们输入什么地方来解决问题! &LT; / clang_salespitch&GT;
答案 1 :(得分:12)
因为在C ++中,白色空间总体上并不重要。所以这是有效的代码:
SomeFunction(x)
;if(bSomeCondition)
{
...
}
因此编译器消息只是报告在if
之前没有出现分号。
答案 2 :(得分:6)
在此代码中:
SomeFunction(x)
if (y) {
}
正如您所说,错误将在第2行报告为missing ';' before 'if'
。
第1行没有错。没有分号是完全有效的,除了一个分号(例如点,数学运算符,赋值或指针等)之外,还有几个表达式是可能的。 )。
因此,报告上一行的错误可能并不总是有意义,请举例:
SomeFunction(x)
+= 10
- 5
// blank line
// blank line
if (y) {
}
哪一行有错误? - 5
的行?还是其中一条评论专栏?对于编译器,错误实际上是'if',因为它是第一个可以检测到错误的地方。要报告不同的行,编译器必须将最后一个正确解析的标记报告为错误,而不是检测到错误的第一个位置。这听起来有点倒退,并且说//blank line1
缺少分号更令人困惑,因为将其更改为//blank line;
当然不会改变或修复错误。
顺便说一句,这不是C或C ++独有的。这是在大多数解析器中报告错误的常用方法。
答案 3 :(得分:4)
很简单,因为解析是如何完成的。如果解析器需要;
,而是遇到if
,则错误位于if
。最简单的报告方式是在;
之前预计会if
。
答案 4 :(得分:3)
编译器是与空白无关的。它不知道(或关心)你的陈述之间有回车或标签或空格。所有它关心的是半冒号之后或之前,或括号之后/之前('{','}')结束和开始类和函数。这就是为什么:)
答案 5 :(得分:3)
因为当解析该行时,它不知道你还想要一个分号。我们来看一个例子:
int mystuff
这行是否缺少分号?这取决于接下来会发生什么。例如,以下构造完全可以:
int mystuff
= 1;
我永远不会这样写,但对于编译器来说没问题。
答案 6 :(得分:3)
因为,以下代码是正确的:
SomeFunction(x)
;if(bSomeCondition)
{
}
那是因为忽略了不必要的空格。
答案 7 :(得分:2)
简短回答:您可以将;
放入第266行,然后就可以了。从编译器的角度来看,错误存在。
您可能想尝试clang
,虽然我不知道它是否为此特定类型的错误提供了更好的错误消息,但通常它会提供很多clearer error messages。
答案 8 :(得分:1)
这是因为编译器检查了整个语句。让我举个例子:
int a,b,c
c=a+b;
cout<<c;
此代码生成编译错误,“;预期在c /第2行之前”,这是因为编译器首先查看第1行的int a,b,c,并且编译器不知道是否会有任何其他变量或语句,所以编译器移动到第二行(因为允许空格),然后它看到有“c = a + b”,这是一个语句,因此编译器知道出现了问题,因为它预期变量或分号(;)。因此,它告诉我们它期待一个;在发表声明之前。
长话短说,编译器在声明后不会查找分号(如果我们的代码中可能无法使用空格),它会查找;在另一个语句之前,因为编译器不知道第一个语句会有多长。