Почему компилятор C++ выдает ошибки после строк, а не на них?

этот вопрос пришел мне в голову сегодня на работе, когда у меня был еще один домашний роман с моим компилятором. Несмотря на мой мизинец (из-за всех нажатий точки с запятой, которые я делаю на работе), мне удалось пропустить один перед if заявление. Очевидно, это привело к ошибке компиляции:

ошибка C2143: синтаксическая ошибка: отсутствует"; "перед " Если"

поэтому я подумал: "Ну, почему вы не можете сказать мне строку, в которой отсутствует точка с запятой, а не линия после проблема. и я начал экспериментировать с другими подобными синтаксическими ошибками:

ошибка C2065: "myUndeclared": необъявленный идентификатор

ошибка C2143: синтаксическая ошибка: отсутствует") "перед " Если"

etc...

Теперь все эти ошибки, аналогичным образом, приведут меня к строке после проблемы и жаловаться на что-то перед if заявление.

рассмотрим следующее:

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

if(bSomeCondition)
{
    ...
}

Я получаю две ошибки компиляции:

(строка 265) ошибка C2065: 'x' : необъявленный идентификатор

(строка 266) ошибка C2143: синтаксическая ошибка : отсутствует"; "перед " Если"

однако первая ошибка правильно сообщает мне номер строки, несмотря на отсутствующую точку с запятой. Это говорит мне о том, что компилятор не запутывается в синтаксическом анализе и может пройти мимо проблема с точкой с запятой. Итак, почему компилятор настаивает на том, чтобы грамматические ошибки сообщались таким образом? Другие ошибки (не грамматические) сообщаются по найденным строкам. Связано ли это с тем, что компилятор делает несколько проходов? В принципе, я надеюсь, что кто-то с рабочим знанием компилятора C++ может объяснить конкретно, что делает компилятор, который требует сообщения об ошибках этим способом "до".

9 ответов


короткий ответ на более общий вопрос "почему сообщения об ошибках C/C++ сосут" - "иногда C++ действительно трудно разобрать" (на самом деле у него нет контекстно-свободной грамматики). Однако это не очень веская причина - все еще можно сделать инструменты, которые записывают лучшую диагностическую информацию, чем большинство компиляторов C++.

более практичный ответ: "авторы компилятора унаследовали устаревшие кодовые базы, которые не оценивали сообщения об ошибках", в сочетании с мягкой дозой " компилятора авторы ленивы", увенчанный"диагностическая отчетность не является захватывающей проблемой". Большинство авторов компиляторов добавят новую языковую функцию или улучшат производительность 3% codegen, а не сделают значительный рефакторинг на базе кода, чтобы обеспечить достойную отчетность об ошибках. Конкретный вопрос о том, "почему ошибки не локализованы должным образом в строке, которая их" вызвала", является примером этого. На самом деле нет технической причины, по которой компиляторы обычно не могут решить, что a ; отсутствует , и расскажите об исходном промежутке последнего ; отсутствует утверждение-даже при наличии общей инвариантности пробелов C++. Просто хранение этой информации (в значительной степени) исторически игнорировалось.

тем не менее, новые компиляторы, не стесненные десятилетиями старого кода, делают намного лучше. Взгляните на компилятор Clang, который гордится разумными сообщениями об ошибках. The страница о диагностике показывает, насколько они лучше, чем 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++ пробел не имеет значения, в целом. Итак, это действительный код:

SomeFunction(x)

;if(bSomeCondition)
{
    ...
}

таким образом, сообщение компилятора просто сообщает, что двоеточие не появилось где-то до if.


в этом коде:

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++. Это распространенный способ сообщать об ошибках в большинстве парсеров.


довольно просто, так как анализ делается. Когда парсер ожидает ; и вместо встреч if ошибка в блоке if. Самый простой разумный способ сообщить об этом-сказать ; ожидается, что до if.


компилятор является агностиком пробела. Он не знает (или не заботится), что есть возврат каретки или вкладки или пробелы между вашими инструкциями. все, что его волнует, - это то, что после или перед полуколонами, или после/перед скобками ('{ ' ,'}'), которые заканчиваются и начинаются классами и функциями. Вот почему :)


потому что, когда он закончит разбор этой строки, он еще не знает, что вы хотели точку с запятой. Давайте рассмотрим пример:

int mystuff

в этой строке отсутствует точка с запятой? Это зависит от того, что будет дальше. Например, следующая конструкция совершенно в порядке:

int mystuff
   = 1;

Я бы никогда не написал это так, но для компилятора это нормально.


потому что следующий код будет правильным:

SomeFunction(x)

;if(bSomeCondition)
{
}

это потому, что ненужные пробелы игнорируются.


короткий ответ: вы можете поставить ; в строку 266, и тогда все будет в порядке. С точки зрения компилятора ошибок нет.

вы можете попробовать clang, хотя я не знаю, дает ли он лучшее сообщение об ошибке для этого конкретного типа ошибки, но в целом он дает много более четкие сообщения об ошибках.


это потому, что компилятор проверяет 1 весь оператор. Приведу пример:--2-->

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

этот код генерирует ошибку компиляции, которая"; ожидается до c / line 2", это происходит потому, что компилятор сначала смотрит на строку 1 int a, b, c, и компилятор не имеет понятия, будет ли какая-либо другая переменная или оператор, и поэтому компилятор переходит ко второй строке(потому что допускаются пробелы), а затем он видит, что есть "c=a+b", который является оператором, и, таким образом, компилятор знает что - то не так, так как он ожидал либо переменной, либо точки с запятой(;). И таким образом, он говорит нам, что он ожидал a ; перед заявлением.