3.x – Резюме к главе 3 и небольшой тест

Добавлено 24 апреля 2021 в 20:31

Краткое резюме

Синтаксическая ошибка – это ошибка, возникающая при написании инструкции, недопустимой в соответствии с грамматикой языка C++. Компилятор ее поймает.

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

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

Для отладки мы можем использовать процесс из пяти этапов:

  1. найти первопричину;
  2. понять проблему;
  3. определить, как исправить;
  4. устранить проблему;
  5. выполнить повторное тестирование.

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

Инструменты статического анализа – это инструменты, которые анализируют ваш код и ищут семантические проблемы, которые могут указывать на проблемы с кодом.

Возможность надежного воспроизведения проблемы – первый и самый важный шаг в отладке.

Есть несколько тактик, которые мы можем использовать для поиска проблем:

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

При использовании инструкций печати используйте std::cerr вместо std::cout. Но лучше, избегайте отладки с помощью инструкций печати.

Файл журнала (лог-файл, log file) – это файл, в котором записываются события, происходящие в программе. Процесс записи информации в лог-файл называется логгированием.

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

Модульное тестирование (юнит-тестирование, unit testing) – это метод тестирования программного обеспечения, с помощью которого тестируются небольшие блоки исходного кода, чтобы определить их правильность.

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

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

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

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

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

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

Шаг с выходом (step out) выполняет весь оставшийся код в функции, которая выполняется в настоящее время, а затем возвращает управление вам, когда выполнен возврат из этой функции.

Выполнение до курсора (run to cursor) выполняет программу до тех пор, пока не достигнет инструкции, выбранной курсором мыши.

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

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

Точка останова – это специальный маркер, который сообщает отладчику остановить выполнение программы при его достижении.

Отслеживание переменной позволяет вам проверять значение переменной во время выполнения программы в режиме отладки. Окно просмотра позволяет вам отслеживать значения переменных или выражений.

Стек вызовов – это список всех активных функций, которые были выполнены для перехода к текущей точке выполнения. Окно стека вызовов – это окно отладчика, которое показывает стек вызовов.

Небольшой тест

Вопрос 1

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

#include <iostream>
 
int readNumber(int x)
{
	std::cout << "Please enter a number: ";
	std::cin >> x;
	return x;
}
 
void writeAnswer(int x)
{
	std::cout << "The sum is:" << x;
}
 
int main()
{
	int x {};
	readNumber(x);
	x = x + readNumber(x);
	writeAnswer(x);
 
	return 0;
}

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

#include <iostream>
 
int readNumber()
{
	std::cout << "Please enter a number: ";
	int x {};
	std::cin >> x;
	return x;
}
 
void writeAnswer(int x)
{
	std::cout << "The sum is:" << x;
}
 
int main()
{
	int x { readNumber() };
	x = x + readNumber();
	writeAnswer(x);
 
	return 0;
}

Вопрос 2

Используйте встроенный отладчик для пошагового выполнения следующей программы. Для входных данных введите 8 и 4. На основе полученной информации исправьте программу:

#include <iostream>
 
int readNumber()
{
	std::cout << "Please enter a number:";
	int x {};
	std::cin >> x;
	return x;
}
 
void writeAnswer(int x)
{
	std::cout << "The quotient is:" << x;
}
 
int main()
{
	int x{ };
	int y{ };
	x = readNumber();
	x = readNumber();
	writeAnswer(x/y);
 
	return 0;
}

Проблема здесь в том, что второй вызов readNumber случайно присваивает значение x вместо y, что приводит к делению на 0, что приводит к сбою программы.

#include <iostream>
 
int readNumber()
{
	std::cout << "Please enter a number:";
	int x {};
	std::cin >> x;
	return x;
}
 
void writeAnswer(int x)
{
	std::cout << "The quotient is:" << x;
}
 
int main()
{
	int x{ readNumber() };
	int y{ readNumber() };
	writeAnswer(x/y);
 
	return 0;
}

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


Вопрос 3

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

#include <iostream>
 
void d()
{ // здесь
}
 
void c()
{
}
 
void b()
{
	c();
	d();
}
 
void a()
{
	b();
}
 
int main()
{
	a();
 
	return 0;
}

d
b
a
main

Примечание автора


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

Теги

C++ / CppDebugДля начинающихМодульное тестирование / Юнит-тестирование / Unit testingОбучениеОтладкаОтладчик / DebuggerПрограммированиеРефакторингСтек вызовов

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

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.