为什么C ++编译器会在行之后而不是在行上给出错误?

时间:2011-08-15 21:43:46

标签: c++ compiler-construction compilation grammar

今天,当我与我的编译器发生另一项国内事务时,这个问题突然出现在我的脑海中。尽管我的粉红色(由于我在工作时所做的所有分号按压),我在if声明之前设法错过了一个。显然,这导致编译错误:

  

错误C2143:语法错误:缺少';'在'if'之前

所以我想知道“好吧,为什么你不能告诉我在问题之后缺少分号而不是行的行。”然后我开始尝试其他类似的语法错误:

  

错误C2065:'myUndeclared':未声明的标识符

     

错误C2143:语法错误:在'if'

之前缺少')'      

等...

现在,所有这些错误同样会在问题之后将我带到行并在if语句之前抱怨某事。

请考虑以下事项:

SomeFunction(x) //Notice, there is no ';' here

if(bSomeCondition)
{
    ...
}

我遇到两个编译错误:

  

(第265行)错误C2065:'x':未声明的标识符

     

(第266行)错误C2143:语法错误:缺少';'在'if'之前

然而,第一个错误正确告诉我行号,尽管缺少分号。这告诉我编译器在解析时不会被绊倒,并且能够使它超过分号问题。那么,为什么编译器坚持以这种方式报告语法错误呢?在找到它们的行上报告其他错误(非语法)。这是否与编译器进行多次传递有关?基本上,我希望具有C ++编译器工作知识的人可以具体解释编译器正在做什么,这需要以“之前”的方式报告错误。

9 个答案:

答案 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”,这是一个语句,因此编译器知道出现了问题,因为它预期变量或分号(;)。因此,它告诉我们它期待一个;在发表声明之前。

长话短说,编译器在声明后不会查找分号(如果我们的代码中可能无法使用空格),它会查找;在另一个语句之前,因为编译器不知道第一个语句会有多长。