Литмир - Электронная Библиотека
A
A

5.2. Источники ошибок

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Перечислим несколько источников ошибок.

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

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

Непредусмотренные аргументы. Функции принимают аргументы. Если функция принимает аргумент, который не был предусмотрен, то возникнет проблема, как, например, при вызове стандартной библиотечной функции извлечения корня из –1,2:

sqrt(–1.2)
. Поскольку функция
sqrt()
получает положительную переменную типа
double
, в этом случае она не сможет вернуть правильный результат. Такие проблемы обсуждаются в разделе 5.5.3.

Непредусмотренные входные данные. Обычно программы считывают данные (с клавиатуры, из файлов, из средств графического пользовательского интерфейса, из сетевых соединений и т.д.). Как правило, программы выдвигают к входным данным много требований, например, чтобы пользователь ввел число. А что, если пользователь введет не ожидаемое целое число, а строку “Отстань!”? Этот вид проблем обсуждается в разделах 5.6.3 и 10.6.

Неожиданное состояние. Большинство программ хранит большое количество данных (“состояний”), предназначенных для разных частей системы. К их числу относятся списки адресов, каталоги телефонов и данные о температуре, записанные в объекты типа

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

Логические ошибки. Эти ошибки приводят к тому, что программа просто делает не то, что от нее ожидается; мы должны найти и исправить эти ошибки. Примеры поиска таких ошибок приводятся в разделе 6.6 и 6.9.

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

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

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

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

В качестве примера рассмотрим вызовы следующей простой функции:

int area(int length, int width); // вычисление площади треугольника

5.3.1. Синтаксические ошибки

Что произойдет, если мы вызовем функцию area() следующим образом:

int s1 = area(7;   // ошибка: пропущена скобка )

int s2 = area(7)   // ошибка: пропущена точка с запятой ;

Int s3 = area(7);  // ошибка: Int — это не тип

int s4 = area('7); // ошибка: пропущена кавычка '

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

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

s3
, компилятор вряд ли напишет что-то вроде следующей фразы:

“Вы неправильно написали слово

int
; не следует употреблять прописную букву
i
.”

Скорее, он выразится так:

“Синтаксическая ошибка: пропущена

';'
перед идентификатором '
s3'

“У переменной

's3'
пропущен идентификатор класса или типа”

“Неправильный идентификатор класса или типа

'Int'

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

“Перед переменной

s3
сделана синтаксическая ошибка, и надо что-то сделать либо с типом
Int
, либо с переменной
s3
.”

Поняв это, уже нетрудно решить проблему.

ПОПРОБУЙТЕ

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

5.3.2. Ошибки, связанные с типами

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

int x0 = arena(7); // ошибка: необъявленная функция

int x1 = area(7);  // ошибка: неправильное количество аргументов

int x2 = area("seven",2); // ошибка: первый аргумент

                          // имеет неправильный тип

60
{"b":"847443","o":1}