Как ни странно, программирование может быть сложным и ошибок может быть очень много. Ошибки, как правило, попадают в одну из двух категорий: синтаксические или семантические/смысловые
Типы ошибок
Синтаксическая ошибка возникает, когда вы пишете код, который не соответствует правилам грамматики языка C++. Например, пропущенные точки с запятой, необъявленные переменные, непарные круглые или фигурные скобки и т.д. В следующей программе есть несколько синтаксических ошибок:1
#include <iostream>; // директивы препроцессора не заканчиваются точкой с запятой
int main ( )
{
std : cout < "Hi there; << x; // недействительный оператор (:), незаконченное предложение (пропущено " ) и необъявленная переменная
return 0 // пропущена точка с запятой в конце стейтмента
}
К счастью, компилятор ловит подобные ошибки и сообщает о них в виде предупреждений или ошибок.
Семантическая ошибка возникает, когда код является синтаксически правильным, но делает не то, что задумал программист.
Иногда это может привести к сбою в программе, например, если делить на ноль:
#include <iostream>
int main ( )
{
int a = 10 ;
int b = 0 ;
std :: cout << a << " / " << b << " = " << a / b ; // делить на 0 нельзя
return 0 ;
}
Иногда это может привести к неверным результатам:
#include <iostream>
int main ( )
{
std :: cout << "Hello, word!" ; // орфографическая ошибка
return 0 ;
}
Либо делать вообще не то, что нужно:
#include <iostream>
int add ( int x , int y )
{
return x - y ; // функция должна выполнять сложение, но выполняет вычитание
}
int main ( )
{
std :: cout << add ( 5 , 3 ) ; // должно быть 8, но результат - 2
return 0 ;
}
К сожалению, компилятор не ловит подобные ошибки, так как он проверяет только то, что вы написали, а не то, что вы хотели этим сделать.
В примерах, приведенных выше, ошибки довольно легко обнаружить. Но в большинстве программ (в которых больше 40 строк кода), семантические ошибки увидеть с помощью простого просмотра кода будет не так-то и легко.
И здесь нам на помощь приходит отладчик.
Отладчик
Отладчик (или «дебаггер» , от англ. «debugger» ) — это компьютерная программа, которая позволяет программисту контролировать выполнение кода. Например, программист может использовать отладчик для выполнения программы пошагово, последовательно изучая значения переменных в программе.
Более ранние дебаггеры, такие как GDB , имели интерфейс командной строки, где программисту приходилось вводить специальные команды для старта работы. Более современные дебаггеры уже имеют «графический» интерфейс, что значительно упрощает работу с ними. Сейчас почти все современные IDE имеют встроенные отладчики. То есть, вы можете использовать одну среду разработки как для написания кода, так и для его отладки (вместо постоянного переключения между разными программами).
Базовый функционал у всех отладчиков один и тот же. Отличаются они, как правило, тем, как этот функционал и доступ к нему организованы, горячими клавишами и дополнительными возможностями.
Примечание: Перед тем как продолжить, убедитесь, что вы находитесь в режиме конфигурации «Debug» . Все скриншоты данного урока выполнены в Visual Studio 2019.
Степпинг
Степпинг (англ. «stepping» ) — это возможность отладчика выполнять код пошагово (строка за строкой). Есть три команды степпинга:
- Команда "Шаг с заходом"
- Команда "Шаг с обходом"
- Команда "Шаг с выходом"
Мы сейчас рассмотрим каждую из этих команд в индивидуальном порядке.
Команда «Шаг с заходом»
Команда «Шаг с заходом» (англ. «Step into» ) выполняет следующую строку кода. Если этой строкой является вызов функции, то «Шаг с заходом» открывает функцию и выполнение переносится в начало этой функции.
Давайте рассмотрим очень простую программу:
#include <iostream>
void printValue ( int nValue )
{
std :: cout << nValue ;
}
int main ( )
{
printValue ( 5 ) ;
return 0 ;
}
Как вы уже знаете, при запуске программы выполнение начинается с вызова главной функции main(). Так как мы хотим выполнить отладку внутри функции main(), то давайте начнем с использования команды «Шаг с заходом».
В Visual Studio, перейдите в меню "Отладка" > "Шаг с заходом" (либо нажмите F11 ):
Если вы используете другую IDE, то найдите в меню команду "Step Into/Шаг с заходом" и выберите её.
Когда вы это сделаете, должны произойти две вещи. Во-первых, так как наше приложение является консольной программой, то должно открыться консольное окно. Оно будет пустым, так как мы еще ничего не выводили. Во-вторых, вы должны увидеть специальный маркер слева возле открывающей скобки функции main(). В Visual Studio этим маркером является жёлтая стрелочка (если вы используете другую IDE, то должно появиться что-нибудь похожее):
Стрелка-маркер указывает на следующую строку, которая будет выполняться. В этом случае отладчик говорит нам, что следующей строкой, которая будет выполняться, — будет открывающая фигурная скобка функции main(). Выберите «Шаг с заходом» еще раз — стрелка переместится на следующую строку:
Это значит, что следующей строкой, которая будет выполняться, — будет вызов функции printValue(). Выберите «Шаг с заходом» еще раз. Поскольку printValue() — это вызов функции, то мы переместимся в начало функции printValue():
Выберите еще раз «Шаг с заходом» для выполнения открывающей фигурной скобки printValue(). Стрелка будет указывать на std::cout << nValue; .
Теперь выберите «Шаг с обходом» (F10). Вы увидите число 5 в консольном окне.
Выберите «Шаг с заходом» еще раз для выполнения закрывающей фигурной скобки printValue(). Функция printValue() завершит свое выполнение и стрелка переместиться в функцию main(). Обратите внимание, в main() стрелка снова будет указывать на вызов printValue():
Может показаться, будто отладчик намеревается еще раз повторить цикл с функцией printValue(), но в действительности он нам просто сообщает, что он только что вернулся из этой функции.
Выберите «Шаг с заходом» два раза. Готово, все строки кода выполнены. Некоторые дебаггеры автоматически прекращают сеанс отладки в этой точке. Но Visual Studio так не делает, так что если вы используете Visual Studio, то выберите "Отладка" > "Остановить отладку" (или Shift+F5 ):
Таким образом мы полностью остановили сеанс отладки нашей программы.
Команда «Шаг с обходом»
Как и команда «Шаг с заходом», команда «Шаг с обходом» (англ. «Step over» ) позволяет выполнить следующую строку кода. Только если этой строкой является вызов функции, то «Шаг с обходом» выполнит весь код функции в одно нажатие и возвратит нам контроль после того, как функция будет выполнена.
Примечание для пользователей Code::Blocks : Команда «Step over» называется «Next Line».
Рассмотрим пример, используя следующую программу:
#include <iostream>
void printValue ( int nValue )
{
std :: cout << nValue ;
}
int main ( )
{
printValue ( 5 ) ;
return 0 ;
}
Нажмите «Шаг с заходом», чтобы дойти до вызова функции printValue():
Теперь вместо команды «Шаг с заходом» выберите «Шаг с обходом» (или F10):
Отладчик выполнит функцию (которая выведет значение 5 в консоль), а затем возвратит нам управление на строке return 0; . И это всё за одно нажатие.
Команда «Шаг с обходом» позволяет быстро пропустить код функций, когда мы уверены, что они работают корректно и их не нужно отлаживать.
Команда «Шаг с выходом»
В отличие от двух предыдущих команд, команда «Шаг с выходом» (англ. «Step out» ) не просто выполняет следующую строку кода. Она выполняет весь оставшийся код функции, в которой вы сейчас находитесь, и возвращает контроль только после того, когда функция завершит свое выполнение. Проще говоря, «Шаг с выходом» позволяет выйти из функции.
Обратите внимание, команда «Шаг с выходом» появится в меню «Отладка» только после начала сеанса отладки (что делается путем использования одной из двух вышеприведенных команд).
Рассмотрим все тот же пример:
#include <iostream>
void printValue ( int nValue )
{
std :: cout << nValue ;
}
int main ( )
{
printValue ( 5 ) ;
return 0 ;
}
Нажимайте «Шаг с заходом» до тех пор, пока не перейдете к открывающей фигурной скобке функции printValue():
Затем выберите "Отладка" > "Шаг с выходом" (либо Shift+F11 ):
Вы заметите, что значение 5 отобразилось в консольном окне, а отладчик перешел к вызову функции printValue() в main():
Команда «Выполнить до текущей позиции»
В то время как степпинг полезен для изучения каждой строки кода по отдельности, в большой программе перемещаться по коду с помощью этих команд будет не очень удобно.
Но и здесь современные отладчики предлагают еще несколько инструментов для эффективной отладки программ.
Команда «Выполнить до текущей позиции» позволяет в одно нажатие выполнить весь код до строки, обозначенной курсором. Затем контроль обратно возвращается к вам, и вы можете проводить отладку с указанной точки уже более детально. Давайте попробуем, используя уже знакомую нам программу:
#include <iostream>
void printValue ( int nValue )
{
std :: cout << nValue ;
}
int main ( )
{
printValue ( 5 ) ;
return 0 ;
}
Поместите курсор на строку std::cout << nValue; внутри функции printValue(), затем щелкните правой кнопкой мыши и выберите "Выполнить до текущей позиции" (либо Ctrl+F10 ):
Вы заметите, что жёлтая стрелочка переместится на указанную вами строку. Выполнение программы остановится в этой точке, и программа будет ждать ваших дальнейших команд.
Команда «Продолжить»
Если вы находитесь в середине сеанса отладки вашей программы, то вы можете сообщить отладчику продолжать выполнение кода до тех пор, пока он не дойдет до конца программы (или до следующей контрольной точки). В Visual Studio эта команда называется «Продолжить» (англ. «Continue» ). В других дебаггерах она может иметь название «Run» или «Go».
Возвращаясь к вышеприведенному примеру, мы находимся как раз внутри функции printValue(). Выберите "Отладка" > "Продолжить" (или F5 ):
Программа завершит свое выполнение и выйдет из сеанса отладки.
Точки останова
Точки останова (англ. «breakpoint s» ) — это специальные маркеры, на которых отладчик останавливает процесс выполнения программы.
Чтобы задать точку останова в Visual Studio, щелкните правой кнопкой мыши по выбранной строке > "Точка останова" > "Вставить точку останова" :
Появится кружочек возле строки:
В программе, приведенной выше, создайте точку останова на строке std::cout << nValue; . Затем выберите «Шаг с заходом» для старта сеанса отладки, а затем «Продолжить». Вы увидите, что вместо завершения выполнения программы и остановки сеанса отладки, отладчик остановится в указанной вами точке:
Точки останова чрезвычайно полезны, если вы хотите изучить только определенную часть кода. Просто задайте точку останова в выбранном участке кода, выберите команду «Продолжить» и отладчик автоматически остановится возле указанной строки. Затем вы сможете использовать команды степпинга для более детального просмотра/изучения кода.