Ошибка во время компиляции

From Wikipedia, the free encyclopedia

Compilation error refers to a state when a compiler fails to compile a piece of computer program source code, either due to errors in the code, or, more unusually, due to errors in the compiler itself. A compilation error message often helps programmers debugging the source code. Although the definitions of compilation and interpretation can be vague, generally compilation errors only refer to static compilation and not dynamic compilation. However, dynamic compilation can still technically have compilation errors,[citation needed] although many programmers and sources may identify them as run-time errors. Most just-in-time compilers, such as the Javascript V8 engine, ambiguously refer to compilation errors as syntax errors since they check for them at run time.[1][2]

Examples[edit]

Common C++ compilation errors[edit]

  • Undeclared identifier, e.g.:

doy.cpp: In function `int main()':
doy.cpp:25: `DayOfYear' undeclared (first use this function)
[3]

This means that the variable «DayOfYear» is trying to be used before being declared.

  • Common function undeclared, e.g.:

xyz.cpp: In function `int main()': xyz.cpp:6: `cout' undeclared (first use this function)[3]

This means that the programmer most likely forgot to include iostream.

  • Parse error, e.g.:

somefile.cpp:24: parse error before `something'[4]

This could mean that a semi-colon is missing at the end of the previous statement.

Internal Compiler Errors[edit]

An internal compiler error (commonly abbreviated as ICE) is an error that occurs not due to erroneous source code, but rather due to a bug in the compiler itself. They can sometimes be worked around by making small, insignificant changes to the source code around the line indicated by the error (if such a line is indicated at all),[5][better source needed] but sometimes larger changes must be made, such as refactoring the code, to avoid certain constructs. Using a different compiler or different version of the compiler may solve the issue and be an acceptable solution in some cases. When an internal compiler error is reached many compilers do not output a standard error, but instead output a shortened version, with additional files attached, which are only provided for internal compiler errors. This is in order to insure that the program doesn’t crash when logging the error, which would make solving the error nigh impossible. The additional files attached for internal compiler errors usually have special formats that they save as, such as .dump for Java. These formats are generally more difficult to analyze than regular files, but can still have very helpful information for solving the bug causing the crash.[6]

Example of an internal compiler error:

somefile.c:1001: internal compiler error: Segmentation fault
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://bugs.gentoo.org/> for instructions.

References[edit]

  1. ^ «Errors | Node.js v7.9.0 Documentation». nodejs.org. Retrieved 2017-04-14.
  2. ^ «SyntaxError». Mozilla Developer Network. Retrieved 2017-04-14.
  3. ^ a b «Common C++ Compiler and Linker Errors». Archived from the original on 2008-02-16. Retrieved 2008-02-12.
  4. ^ «Compiler, Linker and Run-Time Errors».
  5. ^ Cunningham, Ward (2010-03-18). «Compiler Bug». WikiWikiWeb. Retrieved 2017-04-14.
  6. ^ జగదేశ్. «Analyzing a JVM Crash». Retrieved 2017-04-15.

Это ваша первая программа на C (или C++) — она не такая уж большая, и вы собираетесь скомпилировать ее. Вы нажимаете на compile (или вводите команду компиляции) и ждете. Ваш компилятор выдает пятьдесят строк текста. Вы выбираете слова warning и error. Задумываетесь, значит ли это, что все в порядке. Вы ищите полученный исполняемый файл. Ничего. Черт возьми, думаете вы, я должен выяснить, что все это значит …

Типы ошибок компиляции

Во-первых, давайте различать типы ошибок. Большинство компиляторов покажет три типа предупреждений во время компиляции:

  • предупреждения компилятора;
  • ошибки компилятора;
  • ошибки компоновщика.

Хоть вы и не хотите игнорировать их, предупреждения компилятора не являются чем-то достаточно серьезным, чтобы не скомпилировать вашу программу. Прочитайте следующую статью, которая расскажет вам, почему стоит дружить с компилятором и его предупреждениями. Как правило, предупреждения компилятора — это признак того, что что-то может пойти не так во время выполнения. Как компилятор узнает об этом? Вы, должно быть делали типичные ошибки, о которых компилятор знает. Типичный пример — использование оператора присваивания = вместо оператора равенства == внутри выражения. Ваш компилятор также может предупредить вас об использовании переменных, которые не были инициализированы и других подобных ошибках. Как правило, вы можете установить уровень предупреждений вашего компилятора — я устанавливаю его на самый высокий уровень, так что предупреждения компилятора не превращаются в ошибки в выполняемой программе (“ошибки выполнения”).

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

Ошибки — это условия, которые препятствуют завершению компиляции ваших файлов.

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

line 13, unexpected parenthesis ‘)’

Заметьте, что ошибки компилятора всегда будут включать номер строки, в которой была обнаружена ошибка.

Даже если вы прошли процесс компиляции успешно, вы можете столкнуться с ошибками компоновщика. Ошибки компоновщика, в отличие от ошибок компилятора, не имеют ничего общего с неправильным синтаксисом. Вместо этого, ошибки компоновщика — это, как правило, проблемы с поиском определения функций, структур, классов или глобальных переменных, которые были объявлены, но не определены, в файле исходного кода. Как правило, эти ошибки будут иметь вид:

could not find definition for X

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

Ошибки компилятора — с чего начать?

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

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

struct 
{
   int x;
   int y;
} myStruct;

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

Что-то вроде этого:

struct MyStructType
{
   int x;
   int y;
}

int foo()
{}

может привести к огромному количеству ошибок, возможно, включая сообщения:

extraneous ‘int’ ignored

Все это из-за одного символа! Лучше всего начать с самого верха.

 Анализ сообщения об ошибке

Большинство сообщений от компилятора будет состоять как минимум из четырех вещей:

  1. тип сообщения — предупреждение или ошибка;
  2. исходный файл, в котором появилась ошибка;
  3. строка ошибки;
  4. краткое описание того, что работает неправильно.

Вывод g++ для указанной выше программы может выглядеть следующим образом (ваши результаты могут отличаться, если вы используете другой компилятор):

foo.cc:7: error: semicolon missing after struct declaration

foo.cc это имя файла. 7 — номер строки, и ясно, что это ошибка. Короткое сообщение здесь весьма полезно, поскольку оно показывает именно то, что не правильно. Заметим, однако, что сообщение имеет смысл только в контексте программы. Оно не сообщает, в какой структуре не хватает запятой.

Более непонятным является другое сообщение об ошибке из той же попытки компиляции:

extraneous ‘int’ ignored

Программист должен выяснить, почему это произошло. Обратите внимание еще раз, что эта ошибка была вызвана проблемой в начале программы, не в строке 8, а раньше, когда в структуре не хватает точки с запятой. К счастью, понятно, что определение функции для foo было в порядке, это говорит нам о том, что ошибка должна быть где-то в другом месте программы. На самом деле, она должна быть в программе раньше — вы не будете получать сообщение об ошибке, которое указывает на синтаксическую ошибку до строки, на которой ошибка на самом деле произошла.

Это руководящий принцип вычисления ошибок компилятора: если сомневаетесь, посмотрите в программе раньше. Так как синтаксические ошибки могут позже иметь серьезные последствия, вполне возможно, что компилятор указывал номер строки, в которой на самом деле не было синтаксической ошибки!

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

Обработка непонятных или странных сообщений

Есть несколько особенно сложных типов ошибок компилятора. Первый — это необъявленная переменная, которую, как вам кажется, вы объявили. Часто, вы можете указать, где именно переменная была объявлена! Проблема в том, что часто переменная просто написана с ошибкой. К сожалению, это довольно трудно увидеть, так как обычно мы читаем то, что ожидаем, а не то, что есть на самом деле. Кроме того, есть и другие причины, почему это может быть проблемой — например, проблемы с видимостью!

Чтобы разобраться в возможных проблемах, я делаю так: в строке, где находится якобы необъявленная переменная, надо выполнить поиск текстовым редактором слова под курсором (в качестве альтернативы можно скопировать имя переменной и выполнить поиск), и если я записал его неправильно, оно не найдется. Также не надо вводить имя переменной вручную, так как вы случайно можете ввести его правильно.

Второе непонятное сообщение:

unexpected end of file

Что происходит? Почему конец файла будет «неожиданным» ? Ну, здесь главное думать как компилятор; если конец файла является неожиданным, то он,  должно быть, чего-то ждет. Что бы это могло быть? Ответ, как правило, «завершение». Например, закрывающие фигурные скобки или закрывающие кавычки. Хороший текстовый редактор, который выполняет подсветку синтаксиса и автоматический отступ, должен помочь исправить некоторые из этих ошибок, что позволяет легче обнаружить проблемы при написании кода.

В конечном счете, если сообщение непонятное, то подходите к проблеме, думая, как компилятор пытается интерпретировать файл. Это может быть трудно, когда вы только начинаете, но если вы обращаете внимание на сообщения и попробуете понять, что они могли бы означать, вы быстро привыкнете к общим закономерностям.

Наконец, если ничего не работает, вы всегда можете просто переписать несколько строк кода, чтобы убрать любые скрытые синтаксические ошибки, которые вы могли не увидеть. Это может быть опасно, так как вы можете переписать не ту секцию, но это может помочь.

Ошибки компоновщика

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

У вас могут возникнуть проблемы с тем, как вы настроили свой компилятор. Например, даже если включить нужные заголовочные файлы для всех ваших функций, вы все равно должны предоставить вашему компоновщику правильный путь в библиотеку, которая имеет фактическую реализацию. В противном случае, вы получите сообщение об ошибке:

undefined function

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

Ошибки компоновщика могут произойти в функциях, которые вы объявили и определили, если вы не включили все необходимые объектные файлы в процесс связывания. Например, если вы пишете определение класса в myClass.cpp, а ваша основная функция в myMain.cpp, компилятор создаст два объектных файла, myClass.o и myMain.o, а компоновщику будут нужны оба из них для завершения создания новой программы. Если оставить myClass.o, то у него не будет определения класса, даже если вы правильно включите myClass.h!

Иногда появляются незначительные ошибки, когда компоновщик сообщает о более чем одном определении для класса, функции или переменной. Эта проблема может появиться по нескольким причинам: во-первых, у объекта может быть два определения — например, две глобальные переменные объявлены как внешние переменные, чтобы быть доступными за пределами файла исходного кода. Это относится как к функциям, так и к переменным, и это, на самом деле, нередко случается. С другой стороны, иногда это проблема с директивами компоновщика; несколько раз я видел, как люди включают несколько копий одного и того же объектного файла в процесс связывания. И бинго, у вас есть несколько определений. Типичным проявлением этой проблемы является то, что у целого ряда функций есть несколько определений.

Последний странный тип ошибки компоновщика — сообщение

undefined reference to main

Данная ошибка компоновщика отличается от других тем, что она может не иметь ничего общего с объектом, включая файлы или правильные пути к вашей библиотеке. Напротив, это означает, что компоновщик пытался создать исполняемый файл и не смог понять, где расположена функция main(). Это может случиться, если вы забыли включить основную функцию, или, если вы попытаетесь скомпилировать код, который никогда не был отдельным исполняемым файлом (например, если вы попытались скомпилировать библиотеку).

Обработка ошибок и проектирование компилятора

Перевод статьи Error Handling in Compiler Designopen in new window.

Задача по обработке ошибок (Error Handling) включает в себя: обнаружение ошибок, сообщения об ошибках пользователю, создание стратегии восстановления и реализации обработки ошибок. Кроме того система обработки ошибок должна работать быстро.

Типы источников ошибок

Источники ошибок делятся на два типа: ошибки времени выполнения (run-time error) и ошибки времени компиляции (compile-time error).

Ошибки времени выполнения возникают когда программа запущена. Обычно они связаны с неверными входными данными. Примеры таких ошибок: недостаток памяти, конфликт с другим приложением, логические ошибки. Логическая ошибка означает что запуск программы не приводит к ожидаемому результату. Логические ошибки лучше всего обрабатывать тщательным тестированием и отладкой программы.

Ошибки времени компиляции возникают во время компиляции, до запуска программы. Примеры таких ошибок: синтаксическая ошибка или отсутствие файла с кодом на который есть ссылка.

Типы ошибок времени компиляции

Ошибки компиляции разделяются на:

  • Лексические (Lexical): включают в себя опечатки идентификаторов, ключевых слов и операторов
  • Синтаксические (Syntactical): пропущенная точка с запятой или незакрытая скобка
  • Семантические (Semantical): несовместимое значение при присвоении или несовпадение типов между оператором и операндом
  • Логические (Logical): недостижимый код, бесконечный цикл

Парсер, обрабатывая текст, пытается как можно раньше обнаружить ошибку. В современных средах разработки синтаксические ошибки отображаются прямо в редакторе кода, предотвращая последующий неверный ввод. Обнажение ошибки происходит когда введённый префикс не совпадает с префиксами строк верными в выбранном языке программирования. Например префикс for(;) может привести к сообщению об ошибке, так как обычно внутри for должно быть две точки с запятой.

Восстановление после ошибок

Базовое требование к компилятору — прервать компиляцию и выдать сообщение при появлении ошибки. Кроме этого есть несколько методов восстановления после ошибки.

Panic mode recovery

Это самый простой способ восстановления после ошибок и он предотвращает бесконечные циклы в компиляторе при попытках исправить ошибку. Парсер отклоняет следующие за ошибкой символы до того как будет обнаружен специальный символ (например, разделитель команд, точка с запятой). Такой подход адекватен если низкая вероятность нескольких ошибок в одной конструкции.

Пример: рассмотрим выражение с ошибкой (1 + + 2) + 3. При обнаружении второго + пропускаются все символы до следующего числа.

Phase level recovery

Производится локальное изменение входного потока чтобы исправить ошибку.

Error productions

Разработчики компиляторов знают часто встречаемые ошибки. При появлении таких ошибок могут применяться расширения грамматики для их обработки. Например: написание 5x вместо 5*x.

Global correction

Производится как можно меньше изменений чтобы преобразовать код с ошибкой в корректный код. Эту стратегию дорого реализовывать.

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


Комментировать

Ошибки компиляции — ошибки в синтаксисе.
Ошибки исполнения — ошибки в логике.

Это если в двух словах.

jcmvbkbc

jcmvbkbc

@jcmvbkbc

«I’m here to consult you» © Dogbert

Если есть что выполнять — будут ошибки выполнения. Если выполнять нечего — значит были ошибки компиляции (или линковки).


Комментировать

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

  • Отсутствует скобка ( } )
  • Печать значения переменной без его объявления
  • Отсутствует точка с запятой (терминатор)

Ниже приведен пример, демонстрирующий ошибку времени компиляции:

#include<stdio.h>

void main()

{

int x = 10;

int y = 15;

printf ( "%d" , (x, y))

}

Ошибка:

 ошибка: ожидается ';' перед токеном '}'

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

Для большего понимания запустите пример, приведенный ниже.

Ошибка:

 предупреждение: деление на ноль [-Wdiv-by-zero]
     div = n / 0;

В данном примере есть ошибка деления на ноль. Это пример ошибки времени выполнения, т. Е. Ошибок, возникающих во время работы программы.

Различия между ошибкой времени компиляции и ошибкой времени выполнения:

Ошибки времени компиляции Ошибки во время выполнения
Это синтаксические ошибки, которые обнаруживает компилятор. Это ошибки, которые не обнаруживаются компилятором и дают неверные результаты.
Они предотвращают запуск кода, поскольку он обнаруживает некоторые синтаксические ошибки. Они препятствуют полному выполнению кода.
Он включает синтаксические ошибки, такие как отсутствие точки с запятой (;), неправильное написание ключевых слов и идентификаторов и т. Д. Сюда входят такие ошибки, как деление числа на ноль, поиск квадратного корня из отрицательного числа и т. Д.

Improve Article

Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Compile-Time Errors: Errors that occur when you violate the rules of writing syntax are known as Compile-Time errors. This compiler error indicates something that must be fixed before the code can be compiled. All these errors are detected by the compiler and thus are known as compile-time errors. 
    Most frequent Compile-Time errors are: 
     

    • Missing Parenthesis (})
    • Printing the value of variable without declaring it
    • Missing semicolon (terminator)

    Below is an example to demonstrate Compile-Time Error:
     

    C++

    #include <iostream>

    using namespace std;

    int main()

    {

        int x = 10;

        int y = 15;

        cout << " "<< (x, y)

    }

    C

    #include<stdio.h>

    void main()

    {

        int x = 10;

        int y = 15;

        printf("%d", (x, y));

    }

    Error: 
     

    error: expected ';' before '}' token

    Run-Time Errors: Errors which occur during program execution(run-time) after successful compilation are called run-time errors. One of the most common run-time error is division by zero also known as Division error. These types of error are hard to find as the compiler doesn’t point to the line at which the error occurs.
    For more understanding run the example given below.
     

    C++

    #include <iostream>

    using namespace std;

    int main()

    {

        int n = 9, div = 0;

        div = n/0;

        cout <<"result = " << div;

    }

    C

    #include<stdio.h>

    void main()

    {

        int n = 9, div = 0;

        div = n/0;

        printf("result = %d", div);

    }

    Error: 
     

    warning: division by zero [-Wdiv-by-zero]
         div = n/0;

    In the given example, there is Division by zero error. This is an example of run-time error i.e errors occurring while running the program.
    The Differences between Compile-Time and Run-Time Error are:
     

    Compile-Time Errors Runtime-Errors
    These are the syntax errors which are detected by the compiler. These are the errors which are not detected by the compiler and produce wrong results.
    They prevent the code from running as it detects some syntax errors. They prevent the code from complete execution.
    It includes syntax errors such as missing of semicolon(;), misspelling of keywords and identifiers etc. It includes errors such as dividing a number by zero, finding square root of a negative number etc.

    Improve Article

    Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Compile-Time Errors: Errors that occur when you violate the rules of writing syntax are known as Compile-Time errors. This compiler error indicates something that must be fixed before the code can be compiled. All these errors are detected by the compiler and thus are known as compile-time errors. 
    Most frequent Compile-Time errors are: 
     

    • Missing Parenthesis (})
    • Printing the value of variable without declaring it
    • Missing semicolon (terminator)

    Below is an example to demonstrate Compile-Time Error:
     

    C++

    #include <iostream>

    using namespace std;

    int main()

    {

        int x = 10;

        int y = 15;

        cout << " "<< (x, y)

    }

    C

    #include<stdio.h>

    void main()

    {

        int x = 10;

        int y = 15;

        printf("%d", (x, y));

    }

    Error: 
     

    error: expected ';' before '}' token

    Run-Time Errors: Errors which occur during program execution(run-time) after successful compilation are called run-time errors. One of the most common run-time error is division by zero also known as Division error. These types of error are hard to find as the compiler doesn’t point to the line at which the error occurs.
    For more understanding run the example given below.
     

    C++

    #include <iostream>

    using namespace std;

    int main()

    {

        int n = 9, div = 0;

        div = n/0;

        cout <<"result = " << div;

    }

    C

    #include<stdio.h>

    void main()

    {

        int n = 9, div = 0;

        div = n/0;

        printf("result = %d", div);

    }

    Error: 
     

    warning: division by zero [-Wdiv-by-zero]
         div = n/0;

    In the given example, there is Division by zero error. This is an example of run-time error i.e errors occurring while running the program.
    The Differences between Compile-Time and Run-Time Error are:
     

    Compile-Time Errors Runtime-Errors
    These are the syntax errors which are detected by the compiler. These are the errors which are not detected by the compiler and produce wrong results.
    They prevent the code from running as it detects some syntax errors. They prevent the code from complete execution.
    It includes syntax errors such as missing of semicolon(;), misspelling of keywords and identifiers etc. It includes errors such as dividing a number by zero, finding square root of a negative number etc.

    Чтобы разобраться, в чем разница между ошибками времени компиляции и ошибками времени выполнения в Java, разберемся в сути каждого вида.

    Ошибки времени компиляции

    Это синтаксические ошибки в коде, которые препятствуют его компиляции.

    Пример

    public class Test{
       public static void main(String args[]){
          System.out.println("Hello")
       }
    }
    

    Итог

    C:Sample>Javac Test.java
    Test.java:3: error: ';' expected
       System.out.println("Hello")
    

    Ошибки времени выполнения

    Исключение (или исключительное событие) – это проблема, возникающая во время выполнения программы. Когда возникает исключение, нормальный поток программы прерывается, и программа / приложение прерывается ненормально, что не рекомендуется, поэтому эти исключения должны быть обработаны.

    Пример

    import java.io.File;
    import java.io.FileReader;
    
    public class FilenotFound_Demo {
       public static void main(String args[]) {
          File file = new File("E://file.txt");
          FileReader fr = new FileReader(file);
       }
    }
    

    Итог

    C:>javac FilenotFound_Demo.java
    FilenotFound_Demo.java:8: error: unreported exception
    FileNotFoundException; must be caught or declared to be thrown
       FileReader fr = new FileReader(file);
                       ^
    1 error
    

    Обработка ошибок и проектирование компилятора

    Перевод статьи Error Handling in Compiler Designopen in new window.

    Задача по обработке ошибок (Error Handling) включает в себя: обнаружение ошибок, сообщения об ошибках пользователю, создание стратегии восстановления и реализации обработки ошибок. Кроме того система обработки ошибок должна работать быстро.

    Типы источников ошибок

    Источники ошибок делятся на два типа: ошибки времени выполнения (run-time error) и ошибки времени компиляции (compile-time error).

    Ошибки времени выполнения возникают когда программа запущена. Обычно они связаны с неверными входными данными. Примеры таких ошибок: недостаток памяти, конфликт с другим приложением, логические ошибки. Логическая ошибка означает что запуск программы не приводит к ожидаемому результату. Логические ошибки лучше всего обрабатывать тщательным тестированием и отладкой программы.

    Ошибки времени компиляции возникают во время компиляции, до запуска программы. Примеры таких ошибок: синтаксическая ошибка или отсутствие файла с кодом на который есть ссылка.

    Типы ошибок времени компиляции

    Ошибки компиляции разделяются на:

    • Лексические (Lexical): включают в себя опечатки идентификаторов, ключевых слов и операторов
    • Синтаксические (Syntactical): пропущенная точка с запятой или незакрытая скобка
    • Семантические (Semantical): несовместимое значение при присвоении или несовпадение типов между оператором и операндом
    • Логические (Logical): недостижимый код, бесконечный цикл

    Парсер, обрабатывая текст, пытается как можно раньше обнаружить ошибку. В современных средах разработки синтаксические ошибки отображаются прямо в редакторе кода, предотвращая последующий неверный ввод. Обнажение ошибки происходит когда введённый префикс не совпадает с префиксами строк верными в выбранном языке программирования. Например префикс for(;) может привести к сообщению об ошибке, так как обычно внутри for должно быть две точки с запятой.

    Восстановление после ошибок

    Базовое требование к компилятору — прервать компиляцию и выдать сообщение при появлении ошибки. Кроме этого есть несколько методов восстановления после ошибки.

    Panic mode recovery

    Это самый простой способ восстановления после ошибок и он предотвращает бесконечные циклы в компиляторе при попытках исправить ошибку. Парсер отклоняет следующие за ошибкой символы до того как будет обнаружен специальный символ (например, разделитель команд, точка с запятой). Такой подход адекватен если низкая вероятность нескольких ошибок в одной конструкции.

    Пример: рассмотрим выражение с ошибкой (1 + + 2) + 3. При обнаружении второго + пропускаются все символы до следующего числа.

    Phase level recovery

    Производится локальное изменение входного потока чтобы исправить ошибку.

    Error productions

    Разработчики компиляторов знают часто встречаемые ошибки. При появлении таких ошибок могут применяться расширения грамматики для их обработки. Например: написание 5x вместо 5*x.

    Global correction

    Производится как можно меньше изменений чтобы преобразовать код с ошибкой в корректный код. Эту стратегию дорого реализовывать.

    Разница между ошибками времени компиляции и ошибками времени выполнения

    29.12.2019Разница между, Язык программирования

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

    • Отсутствует скобка ( } )
    • Печать значения переменной без ее объявления
    • Отсутствует точка с запятой (терминатор)

    Ниже приведен пример для демонстрации ошибки времени компиляции:

      
    #include<stdio.h>

    void main()

    {

        int x = 10;

        int y = 15; 

        printf("%d", (x, y)) 

    }

    Ошибка:

    error: expected ';' before '}' token

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

    Для большего понимания запустите пример, приведенный ниже.

      
    #include<stdio.h>

    void main()

    {

        int n = 9, div = 0;

        div = n/0;

        printf("resut = %d", div);

    }

    Ошибка:

    warning: division by zero [-Wdiv-by-zero]
         div = n/0;

    В приведенном примере есть деление на ноль ошибок. Это пример ошибки во время выполнения, то есть ошибки, возникающие при запуске программы.

    Различия между временем компиляции и ошибкой во время выполнения:

    Compile-Time Errors Runtime-Errors
    These are the syntax errors which are detected by the compiler. These are the errors which are not detected by the compiler and produce wrong results.
    They prevent the code from running as it detects some syntax errors. They prevent the code from complete execution.
    It includes syntax errors such as missing of semicolon(;), misspelling of keywords and identifiers etc. It includes errors such as dividing a number by zero, finding square root of a negative number etc.

    Рекомендуемые посты:

    • Разница между ОС с разделением времени и ОС реального времени
    • Как избежать ошибки компиляции при определении переменных
    • Разница между процессором и графическим процессором
    • Разница между CLI и GUI
    • Разница между PNG и GIF
    • В чем разница между MMU и MPU?
    • Разница между BFS и DFS
    • Разница между RPC и RMI
    • Разница между C и C #
    • Разница между JSP и ASP
    • Разница между светодиодом и ЖК
    • Разница между MP4 и MP3
    • Разница между LAN, MAN и WAN
    • Разница между 4G и 5G
    • Разница между LAN и WAN

    Разница между ошибками времени компиляции и ошибками времени выполнения

    0.00 (0%) 0 votes

    В C++ различают ошибки времени компиляции и ошибки времени выполнения. Ошибки первого типа обнаруживает компилятор до запуска программы. К ним относятся, например, синтаксические ошибки в коде. Ошибки второго типа проявляются при запуске программы. Примеры ошибок времени выполнения: ввод некорректных данных, некорректная работа с памятью, недостаток места на диске и т. д. Часто такие ошибки могут привести к неопределённому поведению программы.

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

    Коды возврата и исключения

    Рассмотрим функцию, которая считывает со стандартного потока возраст и возвращает его вызывающей стороне. Добавим в функцию проверку корректности возраста: он должен находиться в диапазоне от 0 до 128 лет. Предположим, что повторный ввод возраста в случае ошибки не предусмотрен.

    int ReadAge() {
        int age;
        std::cin >> age;
        if (age < 0 || age >= 128) {
            // Что вернуть в этом случае?
        }
        return age;
    }
    

    Что вернуть в случае некорректного возраста? Можно было бы, например, договориться, что в этом случае функция возвращает ноль. Но тогда похожая проверка должна быть и в месте вызова функции:

    int main() {
        if (int age = ReadAge(); age == 0) {
            // Произошла ошибка
        } else {
            // Работаем с возрастом age
        }
    }
    

    Такая проверка неудобна. Более того, нет никакой гарантии, что в вызывающей функции программист вообще её напишет. Фактически мы тут выбрали некоторое значение функции (ноль), обозначающее ошибку. Это пример подхода к обработке ошибок через коды возврата. Другим примером такого подхода является хорошо знакомая нам функция main. Только она должна возвращать ноль при успешном завершении и что-либо ненулевое в случае ошибки.

    Другим способом сообщить об обнаруженной ошибке являются исключения. С каждым сгенерированным исключением связан некоторый объект, который как-то описывает ошибку. Таким объектом может быть что угодно — даже целое число или строка. Но обычно для описания ошибки заводят специальный класс и генерируют объект этого класса:

    #include <iostream>
    
    struct WrongAgeException {
        int age;
    };
    
    int ReadAge() {
        int age;
        std::cin >> age;
        if (age < 0 || age >= 128) {
            throw WrongAgeException(age);
        }
        return age;
    }
    

    Здесь в случае ошибки оператор throw генерирует исключение, которое представлено временным объектом типа WrongAgeException. В этом объекте сохранён для контекста текущий неправильный возраст age. Функция досрочно завершает работу: у неё нет возможности обработать эту ошибку, и она должна сообщить о ней наружу. Поток управления возвращается в то место, откуда функция была вызвана. Там исключение может быть перехвачено и обработано.

    Перехват исключения

    Мы вызывали нашу функцию ReadAge из функции main. Обработать ошибку в месте вызова можно с помощью блока try/catch:

    int main() {
        try {
            age = ReadAge();  // может сгенерировать исключение
            // Работаем с возрастом age
        } catch (const WrongAgeException& ex) {  // ловим объект исключения
            std::cerr << "Age is not correct: " << ex.age << "n";
            return 1;  // выходим из функции main с ненулевым кодом возврата
        }
        // ...
    }
    

    Мы знаем заранее, что функция ReadAge может сгенерировать исключение типа WrongAgeException. Поэтому мы оборачиваем вызов этой функции в блок try. Если происходит исключение, для него подбирается подходящий catch-обработчик. Таких обработчиков может быть несколько. Можно смотреть на них как на набор перегруженных функций от одного аргумента — объекта исключения. Выбирается первый подходящий по типу обработчик и выполняется его код. Если же ни один обработчик не подходит по типу, то исключение считается необработанным. В этом случае оно пробрасывается дальше по стеку — туда, откуда была вызвана текущая функция. А если обработчик не найдётся даже в функции main, то программа аварийно завершается.

    Усложним немного наш пример, чтобы из функции ReadAge могли вылетать исключения разных типов. Сейчас мы проверяем только значение возраста, считая, что на вход поступило число. Но предположим, что поток ввода досрочно оборвался, или на входе была строка вместо числа. В таком случае конструкция std::cin >> age никак не изменит переменную age, а лишь возведёт специальный флаг ошибки в объекте std::cin. Наша переменная age останется непроинициализированной: в ней будет лежать неопределённый мусор. Можно было бы явно проверить этот флаг в объекте std::cin, но мы вместо этого включим режим генерации исключений при таких ошибках ввода:

    int ReadAge() {
        std::cin.exceptions(std::istream::failbit);
        int age;
        std::cin >> age;
        if (age < 0 || age >= 128) {
            throw WrongAgeException(age);
        }
        return age;
    }
    

    Теперь ошибка чтения в операторе >> у потока ввода будет приводить к исключению типа std::istream::failure. Функция ReadAge его не обрабатывает. Поэтому такое исключение покинет пределы этой функции. Поймаем его в функции main:

    int main() {
        try {
            age = ReadAge();  // может сгенерировать исключения разных типов
            // Работаем с возрастом age
        } catch (const WrongAgeException& ex) {
            std::cerr << "Age is not correct: " << ex.age << "n";
            return 1;
        } catch (const std::istream::failure& ex) {
            std::cerr << "Failed to read age: " << ex.what() << "n";
            return 1;
        } catch (...) {
            std::cerr << "Some other exceptionn";
            return 1;
        }
        // ...
    }
    

    При обработке мы воспользовались функцией ex.what у исключения типа std::istream::failure. Такие функции есть у всех исключений стандартной библиотеки: они возвращают текстовое описание ошибки.

    Обратите внимание на третий catch с многоточием. Такой блок, если он присутствует, будет перехватывать любые исключения, не перехваченные ранее.

    Исключения стандартной библиотеки

    Функции и классы стандартной библиотеки в некоторых ситуациях генерируют исключения особых типов. Все такие типы выстроены в иерархию наследования от базового класса std::exception. Иерархия классов позволяет писать обработчик catch сразу на группу ошибок, которые представлены базовым классом: std::logic_error, std::runtime_error и т. д.

    Вот несколько примеров:

    1. Функция at у контейнеров std::array, std::vector и std::deque генерирует исключение std::out_of_range при некорректном индексе.

    2. Аналогично, функция at у std::map, std::unordered_map и у соответствующих мультиконтейнеров генерирует исключение std::out_of_range при отсутствующем ключе.

    3. Обращение к значению у пустого объекта std::optional приводит к исключению std::bad_optional_access.

    4. Потоки ввода-вывода могут генерировать исключение std::ios_base::failure.

    Исключения в конструкторах

    В главе 3.1 мы написали класс Time. Этот класс должен был соблюдать инвариант на значение часов, минут и секунд: они должны были быть корректными. Если на вход конструктору класса Time передавались некорректные значения, мы приводили их к корректным, используя деление с остатком.

    Более правильным было бы сгенерировать в конструкторе исключение. Таким образом мы бы явно передали сообщение об ошибке во внешнюю функцию, которая пыталась создать объект.

    class Time {
    private:
        int hours, minutes, seconds;
    
    public:
        // Заведём класс для исключения и поместим его внутрь класса Time как в пространство имён
        class IncorrectTimeException {
        };
    
        Time::Time(int h, int m, int s) {
            if (s < 0 || s > 59 || m < 0 || m > 59 || h < 0 || h > 23) {
                throw IncorrectTimeException();
            }
            hours = h;
            minutes = m;
            seconds = s;
        }
    
        // ...
    };
    

    Генерировать исключения в конструкторах — совершенно нормальная практика. Однако не следует допускать, чтобы исключения покидали пределы деструкторов. Чтобы понять причины, посмотрим подробнее, что происходит при генерации исключения.

    Свёртка стека

    Вспомним класс Logger из предыдущей главы. Посмотрим, как он ведёт себя при возникновении исключения. Воспользуемся в этом примере стандартным базовым классом std::exception, чтобы не писать свой класс исключения.

    #include <exception>
    #include <iostream>
    
    void f() {
        std::cout << "Welcome to f()!n";
        Logger x;
        // ...
        throw std::exception();  // в какой-то момент происходит исключение
    }
    
    int main() {
        try {
            Logger y;
            f();
        } catch (const std::exception&) {
            std::cout << "Something happened...n";
            return 1;
        }
    }
    

    Мы увидим такой вывод:

    Logger(): 1
    Welcome to f()!
    Logger(): 2
    ~Logger(): 2
    ~Logger(): 1
    Something happened...
    

    Сначала создаётся объект y в блоке try. Затем мы входим в функцию f. В ней создаётся объект x. После этого происходит исключение. Мы должны досрочно покинуть функцию. В этот момент начинается свёртка стека (stack unwinding): вызываются деструкторы для всех созданных объектов в самой функции и в блоке try, как если бы они вышли из своей области видимости. Поэтому перед обработчиком исключения мы видим вызов деструктора объекта x, а затем — объекта y.

    Аналогично, свёртка стека происходит и при генерации исключения в конструкторе. Напишем класс с полем Logger и сгенерируем нарочно исключение в его конструкторе:

    #include <exception>
    #include <iostream>
    
    class C {
    private:
        Logger x;
    
    public:
        C() {
            std::cout << "C()n";
            Logger y;
            // ...
            throw std::exception();
        }
    
        ~C() {
            std::cout << "~C()n";
        }
    };
    
    int main() {
        try {
            C c;
        } catch (const std::exception&) {
            std::cout << "Something happened...n";
        }
    }
    

    Вывод программы:

    Logger(): 1  // конструктор поля x
    C()
    Logger(): 2  // конструктор локальной переменной y
    ~Logger(): 2  // свёртка стека: деструктор y
    ~Logger(): 1  // свёртка стека: деструктор поля x
    Something happened...
    

    Заметим, что деструктор самого класса C не вызывается, так как объект в конструкторе не был создан.

    Механизм свёртки стека гарантирует, что деструкторы для всех созданных автоматических объектов или полей класса в любом случае будут вызваны. Однако он полагается на важное свойство: деструкторы самих классов не должны генерировать исключений. Если исключение в деструкторе произойдёт в момент свёртки стека при обработке другого исключения, то программа аварийно завершится.

    Пример с динамической памятью

    Подчеркнём, что свёртка стека работает только с автоматическими объектами. В этом нет ничего удивительного: ведь за временем жизни объектов, созданных в динамической памяти, программист должен следить самостоятельно. Исключения вносят дополнительные сложности в ручное управление динамическими объектами:

    void f() {
        Logger* ptr = new Logger();  // конструируем объект класса Logger в динамической памяти
        // ...
        g();  // вызываем какую-то функцию
        // ...
        delete ptr;  // вызываем деструктор и очищаем динамическую память
    }
    

    На первый взгляд кажется, что в этом коде нет ничего опасного: delete вызывается в конце функции. Однако функция g может сгенерировать исключение. Мы не перехватываем его в нашей функции f. Механизм свёртки уберёт со стека лишь сам указатель ptr, который является автоматической переменной примитивного типа. Однако он ничего не сможет сделать с объектом в памяти, на которую ссылается этот указатель. В логе мы увидим только вызов конструктора класса Logger, но не увидим вызова деструктора. Нам придётся обработать исключение вручную:

    void f() {
        Logger* ptr = new Logger();
        // ...
        try {
            g();
        } catch (...) {  // ловим любое исключение
            delete ptr;  // вручную удаляем объект
            throw;  // перекидываем объект исключения дальше
        }
        // ...
        delete ptr;
    
    }
    

    Здесь мы перехватываем любое исключение и частично обрабатываем его, удаляя объект в динамической памяти. Затем мы прокидываем текущий объект исключения дальше с помощью оператора throw без аргументов.

    Согласитесь, этот код очень далёк от совершенства. При непосредственной работе с объектами в динамической памяти нам приходится оборачивать в try/catch любую конструкцию, из которой может вылететь исключение. Понятно, что такой код чреват ошибками. В главе 3.6 мы узнаем, как с точки зрения C++ следует работать с такими ресурсами, как память.

    Гарантии безопасности исключений

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

    Не вдаваясь в детали, давайте посмотрим, как могла бы выглядеть функция добавления элемента.

    template <typename T>
    class List {
    private:
        struct Node {  // узел двусвязного списка
            T element;
            Node* prev = nullptr;  // предыдущий узел
            Node* next = nullptr;  // следующий узел
        };
    
        Node* first = nullptr;  // первый узел списка
        Node* last = nullptr;  // последний узел списка
        int elementsCount = 0;
    
    public:
        // ...
    
        size_t Size() const {
            return elementsCount;
        }
    
        void PushBack(const T& elem) {
            ++elementsCount;
    
            // Конструируем в динамической памяти новой узел списка
            Node* node = new Node(elem, last, nullptr);
    
            // Связываем новый узел с остальными узлами
            if (last != nullptr) {
                last->next = node;
            } else {
                first = node;
            }
            last = node;
        }
    };
    

    Не будем здесь рассматривать другие функции класса — конструкторы, деструктор, оператор присваивания… Рассмотрим функцию PushBack. В ней могут произойти такие исключения:

    1. Выражение new может сгенерировать исключение std::bad_alloc из-за нехватки памяти.

    2. Конструктор копирования класса T может сгенерировать произвольное исключение. Этот конструктор вызывается при инициализации поля element создаваемого узла в конструкторе класса Node. В этом случае new ведёт себя как транзакция: выделенная перед этим динамическая память корректно вернётся системе.

    Эти исключения не перехватываются в функции PushBack. Их может перехватить код, из которого PushBack вызывался:

    #include <iostream>
    
    class C;  // какой-то класс
    
    int main() {
        List<C> data;
        C element;
    
        try {
            data.PushBack(element);
        } catch (...) {  // не получилось добавить элемент
            std::cout << data.Size() << "n";  // внезапно 1, а не 0
        }
    
        // работаем дальше с data
    }
    

    Наша функция PushBack сначала увеличивает счётчик элементов, а затем выполняет опасные операции. Если происходит исключение, то в классе List нарушается инвариант: значение счётчика elementsCount перестаёт соответствовать реальности. Можно сказать, что функция PushBack не даёт гарантий безопасности.

    Всего выделяют четыре уровня гарантий безопасности исключений (exception safety guarantees):

    1. Гарантия отсутствия сбоев. Функции с такими гарантиями вообще не выбрасывают исключений. Примерами могут служить правильно написанные деструктор и конструктор перемещения, а также константные функции вида Size.

    2. Строгая гарантия безопасности. Исключение может возникнуть, но от этого объект нашего класса не поменяет состояние: количество элементов останется прежним, итераторы и ссылки не будут инвалидированы и т. д.

    3. Базовая гарантия безопасности. При исключении состояние объекта может поменяться, но оно останется внутренне согласованным, то есть, инварианты будут соблюдаться.

    4. Отсутсвие гарантий. Это довольно опасная категория: при возникновении исключений могут нарушаться инварианты.

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

    Переместим в нашей функции PushBack изменение счётчика в конец:

        void PushBack(const T& elem) {
            Node* node = new Node(elem, last, nullptr);
    
            if (last != nullptr) {
                last->next = node;
            } else {
                first = node;
            }
            last = node;
    
            ++elementsCount;  // выполнится только если раньше не было исключений
        }
    

    Теперь такая функция соответствует строгой гарантии безопасности.

    В документации функций из классов стандартной библиотеки обычно указано, какой уровень гарантии они обеспечивают. Рассмотрим, например, гарантии безопасности класса std::vector.

    • Деструктор, функции empty, size, capacity, а также clear предоставляют гарантию отсутствия сбоев.

    • Функции push_back и resize предоставляют строгую гарантию.

    • Функция insert предоставляет лишь базовую гарантию. Можно было бы сделать так, чтобы она предоставляла строгую гарантию, но за это пришлось бы заплатить её эффективностью: при вставке в середину вектора пришлось бы делать реаллокацию.

    Функции класса, которые гарантируют отсутсвие сбоев, следует помечать ключевым словом noexcept:

    class C {
    public:
        void f() noexcept {
            // ...
        }
    };
    

    С одной стороны, эта подсказка позволяет компилятору генерировать более эффективный код. С другой — эффективно обрабатывать объекты таких классов в стандартных контейнерах. Например, std::vector<C> при реаллокации будет использовать конструктор перемещения класса C, если он помечен как noexcept. В противном случае будет использован конструктор копирования, который может быть менее эффективен, но зато позволит обеспечить строгую гарантию безопасности при реаллокации.

    ИМХО вам нужно прочитать много ссылок, ресурсов, чтобы понять разницу между временем выполнения и временем компиляции, потому что это очень сложный предмет. Ниже я привожу список некоторых из этих картинок / ссылок, которые я рекомендую.

    Помимо сказанного выше, хочу добавить, что иногда картинка стоит 1000 слов:

    1. порядок этих двух: сначала время компиляции, а затем запуск. Скомпилированная программа может быть открыта и запущена пользователем. Когда приложение запущено, оно называется runtime: время компиляции, а затем runtime1.
      время компиляции, а затем runtime1 ;

    CLR_diag во время компиляции, а затем во время выполнения2
    CLR_diag во время компиляции, а затем во время выполнения2

     from Wiki  
    

    https://en.wikipedia.org/wiki/Run_time
    https://en.wikipedia.org/wiki/Run_time_(program_lifecycle_phase)

    Время выполнения, время выполнения или время выполнения могут относиться к:

    Вычисление

    Время выполнения (фаза жизненного цикла программы), период, в течение которого компьютерная программа выполняется

    Библиотека времени исполнения, программная библиотека, предназначенная для реализации функций, встроенных в язык программирования.

    Система времени выполнения, программное обеспечение, предназначенное для поддержки выполнения компьютерных программ

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

    Введите описание изображения здесь
    Введите описание изображения здесь

    Введите описание изображения здесь
    Введите описание изображения здесь
    Введите описание изображения здесь
    Введите описание изображения здесь
    Введите описание изображения здесь
    Список компиляторов
    Введите описание изображения здесь
    Введите описание изображения здесь
    https://en.wikipedia.org/wiki/List_of_compilers

    • выполните поиск в Google и сравните ошибки времени выполнения с ошибками компиляции:

    ошибки во время выполнения

    ошибки компиляции ;

    1. На мой взгляд, очень важно знать: 3.1 разница между сборкой и компиляцией и жизненным циклом сборки
      https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

    3.2 разница между этими тремя вещами: компиляция vs сборка vs время выполнения

    https://www.quora.com/What-is-the-difference-between-build-run-and-compile
    Фернандо Падоан, разработчик, который немного интересуется языковым дизайном. Ответил 23 февраля. Я возвращаюсь назад в отношении других ответов:

    выполнение означает, что некоторый двоичный исполняемый файл (или сценарий для интерпретируемых языков) будет… ну… выполнен как новый процесс на компьютере; компиляция — это процесс синтаксического анализа программы, написанной на каком-то языке высокого уровня (выше по сравнению с машинным кодом), проверки ее синтаксиса, семантики, связывания библиотек, возможно, выполнения некоторой оптимизации, а затем создания двоичной исполняемой программы в качестве вывода. Этот исполняемый файл может быть в форме машинного кода или какого-либо байтового кода, то есть инструкций, предназначенных для какой-то виртуальной машины; Сборка обычно включает в себя проверку и предоставление зависимостей, проверку кода, компиляцию кода в двоичный файл, выполнение автоматических тестов и упаковку полученных двоичных файлов и других ресурсов (изображений, файлов конфигурации, библиотек и т. д.) в некоторый конкретный формат развертываемого файла. Обратите внимание, что большинство процессов являются необязательными, а некоторые зависят от целевой платформы, для которой вы создаете. Например, при упаковке приложения Java для Tomcat будет выведен файл .war. При создании исполняемого файла Win32 из кода C ++ можно просто вывести программу .exe или упаковать ее в установщик .msi.

    может ли кто-нибудь дать мне хорошее представление о том, в чем разница между временем выполнения и временем компиляции?

    24 ответов


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

    1. какие инварианты удовлетворяет программа?
    2. что может пойти не так на этом этапе?
    3. если этап успешен, что постусловия (что мы знаем)?
    4. каковы входы и выходы, если таковые имеются?

    время компиляции

    1. программа не должна удовлетворять никаким инвариантам. На самом деле, это вовсе не обязательно должна быть хорошо сформированная программа. Вы можете передать этот HTML компилятору и посмотреть, как он блюет…
    2. что может пойти не так во время компиляции:
      • синтаксические ошибки
      • ошибки проверки типов
      • (редко) компилятор сбои
    3. если компилятор преуспеет, что мы знаем?
      • программа была хорошо сформирована — — — значимая программа на любом языке.
      • можно запустить программу. (Программа может провалиться немедленно, но, по крайней мере, мы можем попытаться.)
    4. каковы входы и выходы?
      • Input была скомпилирована программа, а также любые файлы заголовков, интерфейсы, библиотеки или другие вуду, которые она нужно импорт для того, чтобы получить скомпилированный.
      • выход, надеюсь, код сборки или перемещаемый объектный код или даже исполняемую программу. Или если что-то пойдет не так, выход-куча сообщений об ошибках.

    времени

    1. мы ничего не знаем об инвариантах программы-они являются тем, что программист вставил. Инварианты времени выполнения редко применяются только компилятором; ему нужна помощь от программист.
    2. Что может пойти не так, это выполнить ошибки:

      • деление на ноль
      • разыменование нулевого указателя
      • заканчивается

      также могут быть ошибки, обнаруженные самой программой:

      • попытка открыть файл, которого нет
      • попытка найти веб-страницу и обнаружить, что предполагаемый URL-адрес не очень хорошо сформировано
    3. если время выполнения успешно, программа завершает (или продолжает работать) без сбоев.
    4. входы и выходы полностью зависит от программиста. Файлы, окна на экране, сетевые пакеты, задания, отправленные на принтер, вы называете это. Если программа запускает ракеты, это выход, и это происходит только во время выполнения :-)

    Я думаю об этом с точки зрения ошибок, и когда их можно поймать.

    время компиляции:

    string my_value = Console.ReadLine();
    int i = my_value;
    

    строковое значение не может быть присвоено переменной типа int, поэтому компилятор знает во время компиляции что этот код имеет проблемы

    время работы:

    string my_value = Console.ReadLine();
    int i = int.Parse(my_value);
    

    здесь результат зависит от того, какая строка была возвращена ReadLine(). Некоторые значения могут быть проанализированы в int, другие-нет. Это можно определить только при выполнить время


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

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

    вам нужно более четкое определение?


    (редактировать: следующее относится к C# и аналогичным строго типизированным языкам программирования. Я не уверен, что это поможет вам).

    например, компилятор обнаружит следующую ошибку (at время компиляции) перед запуском программы и приведет к ошибке компиляции:

    int i = "string"; --> error at compile-time
    

    С другой стороны, компилятор не может обнаружить такую ошибку, как следующая. Вы получите сообщение об ошибке / исключении в времени (при запуске программы).

    Hashtable ht = new Hashtable();
    ht.Add("key", "string");
    // the compiler does not know what is stored in the hashtable
    // under the key "key"
    int i = (int)ht["key"];  // --> exception at run-time
    

    перевод исходного кода в stuff-happening-on-the — [screen / disk / network] может происходить (примерно) двумя способами; назовите их компиляцией и интерпретацией.

    на составлен программа (примеры-c и fortran):

    1. исходный код подается в другую программу (обычно называемую компилятором—go figure), которая создает исполняемую программу (или ошибку).
    2. исполняемый файл запускается (дважды щелкнув его или введя его имя на командная строка)

    вещи, которые происходят на первом шаге, как говорят, происходят во время» компиляции», вещи, которые происходят на втором шаге, как говорят, происходят во время»выполнения».

    на понял программа (пример MicroSoft basic (на dos) и python (я думаю)):

    1. исходный код подается в другую программу (обычно называемую интерпретатором), которая «запускает» его напрямую. Здесь посредником выступает переводчик слой между вашей программой и операционной системой (или аппаратным обеспечением на действительно простых компьютерах).

    в этом случае разница между временем компиляции и временем выполнения довольно трудно определить и гораздо менее актуальна для программиста или пользователя.

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

    существует также промежуточный случай, в котором программа компилируется в байт-код и запускается немедленно (как в awk или perl).


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

    например.

    свертка констант:

    Если я напишу:

    int i = 2;
    i += MY_CONSTANT;
    

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


    Хм, хорошо, среда выполнения используется для описания чего-то, что происходит при запуске программы.

    время компиляции используется для описания того, что происходит при построении программы (обычно компилятором).


    после предыдущего аналогичного ответа на вопрос в чем разница между ошибкой времени выполнения и ошибки компилятора?

    компиляция / время компиляции / синтаксис / семантические ошибки: ошибки компиляции или времени компиляции-это ошибка, возникшая из-за ошибки ввода, если мы не следуем правильному синтаксису и семантике любого языка программирования, то ошибки времени компиляции генерируются компилятором. Они не позволят вашей программе выполнить одну строку, пока вы удалите все синтаксические ошибки или пока вы не отладите ошибки времени компиляции.
    Пример: отсутствует точка с запятой в C или опечатка int as Int.

    runtime ошибки: ошибки выполнения-это ошибки, которые генерируются, когда программа находится в рабочем состоянии. Эти типы ошибок заставят вашу программу вести себя неожиданно или даже может убить вашу программу. Их часто называют исключениями.
    Пример: Предположим, Вы читаете файл, который не существует, будет результат в ошибке выполнения.

    подробнее обо всех ошибки программирования здесь


    Время Компиляции:

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

    Время Выполнения:

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

    С другой стороны; если что-то сделано во время компиляции, оно выполняется только на вашем компьютере, а если что-то выполняется, оно выполняется на ваших пользователях машина.

    актуальность

    примером того, где это важно, будет тип переноса единицы. Версия времени компиляции (например,импульс.Единицы или моя версия В D) заканчивается так же быстро, как решение проблемы с собственным кодом с плавающей запятой, в то время как версия времени выполнения заканчивается тем, что нужно упаковать информацию о единицах, в которых находится значение, и выполнить проверки в них вдоль каждой операции. С другой стороны, время компиляции версии требуют, чтобы единицы значений были известны во время компиляции и не могли иметь дело с случаем, когда они поступают из ввода во время выполнения.


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

    5

    автор: Stefan Steinegger


    в просто разнице слов b / w время компиляции и время выполнения.

    время компиляции:разработчик пишет программу .формат java & преобразует в байт-код, который является файлом класса, во время этой компиляции любая ошибка может быть определена как ошибка времени компиляции.

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


    вот цитата из Даниэля Ляна, автора «введения в JAVA-Программирование», по вопросу компиляции:

    «программа, написанная на языке высокого уровня, называется исходной программой или исходным кодом. Поскольку компьютер не может выполнить исходную программу, исходная программа должна быть переведен на код на исполнение. Перевод может быть выполнен с помощью другого инструмента программирования, называемого интерпретатором или компилятор.(Даниэль Лян, «введение в Программирование JAVA», p8).

    …Он Продолжает:..

    «компилятор переводит всю исходный код на машина-код file, и файл машинного кода затем выполняется»

    когда мы пробиваем высокоуровневый / читаемый человеком код, это сначала бесполезно! Это должно быть переведено в последовательность «электронных событий» в ваш крошечный процессор! Первым шагом в этом направлении является компиляция.

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

    помните: просто потому, что программа компилируется без ошибок не означает, что она будет работать без ошибок.

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

    пример ошибки времени компиляции:

    синтаксическая ошибка-как ваш код может быть скомпилирован в инструкции машинного уровня, если они неоднозначны?? Ваш код должен соответствовать 100% синтаксическим правилам языка, иначе он не может быть скомпилирован в working код.

    пример ошибки во время выполнения:

    заканчивается память-вызов рекурсивной функции, например, может привести к переполнению стека, учитывая переменную определенной степени! Как это может предвидеть компилятор!? не может.

    и в этом разница между ошибкой времени компиляции и ошибкой времени выполнения


    в качестве дополнения к другим ответам, вот как я бы объяснил это непрофессионалу:

    ваш исходный код похож на чертеж корабля. Он определяет, как должен быть сделан корабль.

    Если вы передадите свой план верфи, и они найдут дефект во время строительства корабля, они остановятся и сообщат вам об этом немедленно, прежде чем корабль когда-либо покинул сухой док или коснулся воды. Это-ошибка времени компиляции. Корабль никогда даже не плавал или используя свои двигатели. Ошибка была обнаружена, потому что она препятствовала даже созданию корабля.

    когда ваш код компилируется, это похоже на завершение корабля. Собран и готов к работе. Когда вы выполняете свой код, это похоже на запуск корабля в плавание. Пассажиры на борту, двигатели работают, а корпус находится на воде, так что это время выполнения. Если у вашего корабля есть фатальный недостаток, который тонет его в первом рейсе (или, возможно, в каком-то рейсе после дополнительных головных болей), то он страдал ошибка выполнения.


    время выполнения означает, что что-то происходит при запуске программы.

    время компиляции означает, что что-то происходит при компиляции программы.


    Время Компиляции:

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

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

    с другой стороны; если что-то сделано во время компиляции, оно запускается только на вашем компьютере, а если что-то выполняется, оно запускается на вашем компьютере пользователей.


    вот расширение ответа на вопрос «разница между временем выполнения и временем компиляции?- …Различия в накладные расходы связанный с временем выполнения и временем компиляции?

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

    большой источник для дальнейшего чтения здесь:


    Я всегда думал об этом относительно накладных расходов на обработку программ и о том, как это влияет на преформацию, как указано выше. Простым примером может быть определение абсолютной памяти, необходимой для моего объекта в коде, или нет.

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

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

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

    на этой ноте, однако, это действительно зависит от контекст, в котором вы используете время выполнения и время компиляции.


    мы можем классифицировать их под разными двумя широкими группами статической привязки и динамической привязки. Он основан на том, когда привязка выполняется с соответствующими значениями. Если ссылки разрешены во время компиляции, то это статическая привязка, а если ссылки разрешены во время выполнения, то это динамическая привязка. Статическая привязка и динамическая привязка также называются ранней привязкой и поздней привязкой. Иногда они также называются статическим полиморфизмом и динамическим полиморфизм.

    Джозеф Kulandai.


    основная разница между временем выполнения и временем компиляции:

    1. если в вашем коде есть синтаксические ошибки и проверки типа, то он выдает ошибку времени компиляции, где-как время выполнения:он проверяет после выполнения кода.
      Например:

    int a = 1
    int b = a/0;

    здесь первая строка не имеет двоеточия в конце — — — > Ошибка времени компиляции после выполнения программы при выполнении операции b, результат бесконечен — — — > время выполнения ошибка.

    1. время компиляции не ищет вывод функций, предоставляемых вашим кодом, в то время как время выполнения.

    вот очень простой ответ:

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

    скомпилированная программа может быть открыта и запущена пользователем. Когда приложение запущено, оно называется runtime.

    термины «время выполнения» и «время компиляции» часто используются программистами для обозначения различных типов ошибок. Ошибка времени компиляции — это проблема, такая как синтаксическая ошибка или отсутствующая ссылка на файл, которая мешает программе успешно скомпилировать. Компилятор создает ошибки времени компиляции и обычно указывает, какая строка исходного кода вызывает проблему.

    если исходный код программы уже был скомпилирован в исполняемую программу, он может по-прежнему иметь ошибки, которые происходят во время работы программы. Примеры включают функции, которые не работают, неожиданное поведение программы или сбои программы. Эти типы проблем называются ошибками выполнения, поскольку они возникают во время выполнения.

    в ссылка


    помимо того, что сказано выше, я хочу добавить, что иногда картина стоит 1000 слов:

    1. порядок этих двух: Сначала время компиляции, а затем вы запускаете
      Скомпилированная программа может быть открыта и запущена пользователем. Когда приложение запущено, оно называется runtime :
      время компиляции, а затем runtime1
      compile time and then runtime1 ;

    CLR_diag время компиляции, а затем runtime2
    CLR_diag compile time and then runtime2

    1. поиск в google и сравнение ошибок выполнения против компиляции ошибки:

    runtime errors

    compile errors ;

    1. на мой взгляд, очень важно знать :
      3.1 разница между build vs compile и жизненным циклом сборки
      https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

    3.2 разница между этими 3 вещами: compile vs build vs runtime

    https://www.quora.com/What-is-the-difference-between-build-run-and-compile
    Фернандо Падоан, разработчик, который просто немного любопытен для языкового дизайна
    Ответил 23 Февраля
    Я возвращаюсь назад по отношению к другим ответам:

    running получает некоторый двоичный исполняемый файл (или скрипт, для интерпретируемых языков), который будет, ну… выполнен как новый процесс на компьютере;
    компиляция-это процесс синтаксического анализа программы, написанной на некотором языке высокого уровня (выше если сравнивать с машинным кодом), проверяя его синтаксис, семантику, связывая библиотеки, возможно, делая некоторую оптимизацию, а затем создавая двоичную исполняемую программу в качестве вывода. Этот исполняемый файл может быть в виде машинного кода или какого-то байтового кода, то есть инструкций, нацеленных на какую-то виртуальную машину;
    построение обычно включает проверку и предоставление зависимостей, проверку кода, компиляцию кода в двоичный файл, запуск автоматических тестов и упаковку полученного двоичного файла[ies] и другие активы (изображения, файлы конфигурации, библиотеки и т. д.) в определенный формат, развернуть файл. Обратите внимание, что большинство процессов являются необязательными, а некоторые зависят от целевой платформы, для которой вы создаете. В качестве примера упаковка Java-приложения для Tomcat выведет a .War-файл. Создание исполняемого файла Win32 из кода C++ может просто вывести .exe программа, или может также упаковать его внутри .установщик msi.


    время компиляции:
    Время, необходимое для преобразования исходного кода в машинный код, чтобы он стал исполняемым, называется временем компиляции.

    время работы:
    Когда приложение запущено, оно называется временем выполнения.

    ошибки времени компиляции-это синтаксические ошибки, отсутствующие ошибки ссылки на файл.
    Ошибки выполнения происходят после компиляции исходного кода в исполняемую программу и во время выполнения программы. Примеры программы сбои, неожиданное поведение программы или функции не работают.

    1

    автор: Steffi Keran Rani J


    посмотрите на этот пример:

    тест открытого класса {

    public static void main(String[] args) {
        int[] x=new int[-5];//compile time no error
        System.out.println(x.length);
    }}
    

    приведенный выше код скомпилирован успешно, синтаксической ошибки нет, он абсолютно корректен.
    Но во время выполнения он выдает следующую ошибку.

    Exception in thread "main" java.lang.NegativeArraySizeException
        at Test.main(Test.java:5)
    

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


    Это не хороший вопрос для S. O. (Это не конкретный вопрос программирования), но это не плохой вопрос в целом.

    Если вы думаете, что это тривиально: как насчет времени чтения и времени компиляции, и когда это полезное различие? Насчет языков, где компилятор доступен во время выполнения? Гай Стил (не манекен, он) написал 7 страниц в CLTL2 об EVAL-WHEN, которые программисты CL могут использовать для управления этим. 2 предложения едва хватает для определение, который сам по себе далеко не объяснение.

    В общем, это сложная проблема, которую языковые дизайнеры, казалось, пытались избежать.
    Они часто просто говорят: «вот компилятор, он делает вещи во время компиляции; все после этого во время выполнения, получайте удовольствие». C разработан, чтобы быть простым в реализации, а не самой гибкой средой для вычислений. Когда у вас нет компилятора, доступного во время выполнения, или возможности легко управлять, когда выражение оценивается, вы, как правило, в конечном итоге с хаки на языке, чтобы подделать общее использование макросов, или пользователи придумывают шаблоны проектирования для имитации более мощных конструкций. Простой в реализации язык определенно может быть стоящей целью, но это не означает, что это конец всего дизайна языка программирования. (Я не использую EVAL-когда много, но я не могу представить жизнь без него.)

    и problemspace во времени компиляции и времени выполнения-это огромный и по-прежнему во многом неизведанный. Это не значит, что S. O.-правильное место для обсуждения, но я призываю людей исследовать эту территорию дальше, особенно тех, у кого нет предвзятых представлений о том, что это должно быть. Вопрос не простой и не глупый, и мы могли бы, по крайней мере, указать инквизитору правильное направление.

    к сожалению, я не знаю никаких хороших ссылок на это. Переговоры CLTL2 об этом немного, но не узнав об этом.


    Понравилась статья? Поделить с друзьями:
  • Ошибка влюбленного повара 7 букв сканворд
  • Ошибка виндовс не удается подключиться к принтеру
  • Ошибка виндовс не удается завершить форматирование
  • Ошибка виндовс не может подключиться к принтеру
  • Ошибка виндовс не может отформатировать этот диск