【问题标题】:Why does the C++ compiler give errors after lines instead of on them?为什么 C++ 编译器在行之后而不是在行之后给出错误?
【发布时间】:2011-10-27 14:56:05
【问题描述】:

今天在工作中,当我与我的编译器发生另一件事时,这个问题突然出现在我的脑海中。尽管我的小指是浅黄色(由于我在工作中按分号),但在if 声明之前我还是错过了一个。显然,这导致了编译错误:

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

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

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

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

等等……

现在,所有这些错误同样会将我带到问题的之后行,并在if 声明之前抱怨一些事情。

考虑以下几点:

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

if(bSomeCondition)
{
    ...
}

我得到两个编译错误:

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

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

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

【问题讨论】:

  • 您是否尝试在“if”行添加分号,即;if?

标签: c++ compiler-construction compilation grammar


【解决方案1】:

这是因为编译器会检查 1 个完整的语句。举个例子吧:

int a,b,c
c=a+b;
cout<<c;

此代码生成编译错误,即“; 应在 c/第 2 行之前”,这是因为编译器首先查看第 1 行的 int a,b,c 并且编译器不知道是否会有任何其他变量或语句,因此编译器移动到第二行(因为允许使用空格),然后它看到有“c = a + b”,这是一个语句,因此编译器知道有问题,正如它所期望的那样变量或分号 (;)。因此,它告诉我们它期待一个 ;在声明之前。

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

【讨论】:

    【解决方案2】:

    对于“为什么 C/C++ 错误消息很糟糕”这个更普遍的问题的简短回答是“有时 C++ 真的很难解析”(它实际上没有上下文无关的语法)。然而,这并不是一个真正的正当理由——人们仍然可以制作出比大多数 C++ 编译器记录更好的诊断信息的工具。

    更实际的答案是“编译器作者继承了不重视错误消息的遗留代码库”,再加上“编译器作者很懒惰”,再加上“诊断报告不是一个令人兴奋的问题”。大多数编译器编写者会添加新的语言功能或 3% 的代码生成性能改进,而不是对代码库进行重大重构以允许体面的错误报告。关于“为什么错误没有正确定位到'导致'它们的行”的具体问题就是一个例子。编译器通常无法确定缺少 ; 并不是真正的技术原因 ,然后告诉你最后一个; 缺少语句的源跨度——即使存在 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
      }
       ^
       ;
    

    看,它甚至告诉我们要输入什么来解决问题!

    【讨论】:

    • 这些都是有趣的点。但是,它们都不适用于这种情况!
    • 为什么?我们可以清楚地看到,C++ 的空白不变性导致行报告问题并没有压倒一切的技术原因。这只是一种编程选择,很大程度上是由遗留 C++ 解析器强制执行的。 Clang 解析器可以很好地处理提问者的情况。
    • @Adam Wright 啊,是的,现在很好。我喜欢这个叮当声......它太糟糕了我不能使用它!我想我同意你关于懒惰的观点......尽管 c++ 与空白无关,但我确实认为只要付出一点努力,就可以找到更实用的线路来抱怨。看起来 clang 就是这样做的!
    • @Chad:如果您认为这有点费力,那是因为无需维护大型(遗留)代码库。
    • 对此有什么看法:ideone.com/XxoKM?它是否也需要分号? - 我的观点是,虽然有一些常见的错误有共同的原因,但在一般情况下,如果语法错误,你只是不知道代码应该做什么,就无法判断应该如何修复它。 - 另外我认为这是编译器消息比语言难以解析更重要的原因。 IMO,有一些非常直接的语言,其中错误消息很糟糕。
    【解决方案3】:

    在这段代码中:

    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++ 独有的。这是大多数解析器报告错误的常用方法。

    【讨论】:

    • 一个更有趣的问题可能是为什么有些编译器比其他编译器更能说明错误发生在哪里。我的猜测是预处理器与它有很大关系。一些编译器仅在需要时从源代码中获取预处理器标记,而其他编译器可能会更急切地吞噬但记录事物来自哪一行。你同意这个理论吗?
    • 如果您指的是 C 预处理器,那么不,我不同意。我听说过的几乎所有 C 预处理器都以相同的方式工作,并且大多数 C++ 错误(尤其是丑陋的模板错误)与 C 预处理无关。
    【解决方案4】:

    很简单,因为解析是如何完成的。当解析器需要;,但遇到if 时,错误在if 中。报告它的最简单、理智的方法是说 ; 在 if 之前是预期的。

    【讨论】:

      【解决方案5】:

      因为,下面的代码是正确的:

      SomeFunction(x)
      
      ;if(bSomeCondition)
      {
      }
      

      这是因为不必要的空格被忽略了。

      【讨论】:

        【解决方案6】:

        因为当它解析完那一行时,它还不知道你想要一个分号。我们来看一个例子:

        int mystuff
        

        这一行是否缺少分号?这取决于接下来会发生什么。例如下面的结构是完全可以的:

        int mystuff
           = 1;
        

        我永远不会这样写,但是对于编译器来说没问题。

        【讨论】:

          【解决方案7】:

          简短的回答:您可以将; 放入第 266 行,然后就可以了。从编译器的角度来看,错误就在那里。

          您可能想尝试clang,虽然我不知道它是否为这种特定类型的错误提供了更好的错误消息,但总的来说它提供了很多clearer error messages。

          【讨论】:

            【解决方案8】:

            因为在 C++ 中,空白并不重要,总的来说。所以这是有效的代码:

            SomeFunction(x)
            
            ;if(bSomeCondition)
            {
                ...
            }
            

            所以编译器消息只是报告if之前的某个地方没有出现分号。

            【讨论】:

            • 是的,但编译器肯定会发现丢失的分号必须位于line 1 和line 3 之间,而不是just somewhere before line 3
            【解决方案9】:

            编译器与空格无关。它不知道(或关心)您的语句之间是否有回车或制表符或空格。它所关心的只是分号之后或之前,或括号('{','}')之后/之前,它们结束和开始类和函数。这就是为什么:)

            【讨论】:

              猜你喜欢
              • 2016-02-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-08-10
              • 2013-08-18
              • 2011-04-10
              相关资源
              最近更新 更多