Задание найти ошибку в тексте программы c
Я занимаюсь разработкой и когда пишу код, то он иногда не работает так, как я задумывал или даже вообще не работает. Сижу и смотрю на него, гадаю: что и где не так?
Не могу усидеть - иду сразу на ресурсы профессионалов, например Stack Overflow и публикую вопрос "Где здесь ошибка?" или "Почему не работает?"
Но оказывается часто, что проблема мелкая: дурацкая опечатка, ошибка в синтаксисе или еще какая мелочь. Профессионалом так не станешь, если по каждой ерунде бегать по ресурсам. А я хочу им быть.
Ну вот зачем здесь эти "Здравствуйте" и "Спасибо"? Чтобы вопрос больше походил на вопрос от нуба что ли?
1 ответ 1
Вчера всё работало, а сегодня не работает / Код не работает как задумано
В чем заключается процесс отладки? Что это такое?
Процесс отладки состоит в том, что мы останавливаем выполнения скрипта в любом месте, смотрим, что находится в переменных, в функциях, анализируем и переходим в другие места; ищем те места, где поведение отклоняется от правильного.
Заметка: Отладка производится как правило в IDE (Интегрированная среда разработки). Что это такое можно чуть подробнее ознакомиться в вопросе
Какие есть способы предупреждения ошибок, их нахождения и устранения?
В данном случае будет рассмотрен пример с Visual Studio, но отладить код можно и в любой другой IDE.
Подготовка
Достаточно иметь в наличии IDE, например Visual Studio
Запуск
Отладка запускается сочетанием F5 или выбором в верхнем меню Debug → Start Debugging
В данном случае, т.к. функция вызывается сразу на той же странице, то при нажатии кнопки Debug — отладчик моментально вызовет метод, выполнение "заморозится" на первом же брейкпойнте. В ином случае, для активации требуется исполнить действие, при котором произойдет исполнение нужного участка кода (клик на кнопку в UI, передача POST запроса с данными и прочие другие действия)
- Стэк вызовов, все вложенные вызовы, которые привели к текущему месту кода.
- Переменные. На текущий момент строки ниже номера 8 ещё не выполнилась, поэтому определена лишь data и numsStringArr
- Показывает текущие значения любых переменных и выражений. В любой момент здесь можно вписать имя любой переменной в поле name и посмотреть её значение в реальном времени. Например data или nums[0] , а можно и nums[i] и item.test.data.name[5].infoЗадание найти ошибку в тексте программы c] и т.д. На текущий момент строки ниже номера 24 ещё не выполнилась, поэтому num (которая появляется лишь в цикле foreach ) во вкладке Watch обозначена красным цветом с надписью "The name 'num' does not exist in the current context".
Процесс
Для самого процесса используются элементы управления (см. изображение выше, выделено зеленым прямоугольником)
Show Next Statement ( Alt+Num * ) — переносит в файл и текущую линию отлаживаемого скрипта. Например если файлов много, решили посмотреть что в других вкладках, а потом забыли где у вас отладка :)
Step Over ( F10 ) — делает один шаг не заходя внутрь функции. Т.е. если на текущей линии есть какая-то функция, а не просто переменная со значением, то при клике данной кнопки, отладчик не будет заходить внутрь неё.
Step Into ( F11 ) — делает шаг. Но в отличие от предыдущей, если есть вложенный вызов (например функция), то заходит внутрь неё.
Step Out ( Shift+F11 ) — выполняет команды до завершения текущей функции. Удобна, если случайно вошли во вложенный вызов и нужно быстро из него выйти, не завершая при этом отладку.
Restart ( Ctrl+Shift+F5 ) — Перезапустить отладку
Continue ( F5 ) — Продолжает выполнения скрипта с текущего момента. Если больше нет других точек останова, то отладка заканчивается и скрипт продолжает работу. В ином случае работа прерывается на следующей точке останова.
Stop Debugging ( Shift+F5 ) — Завершить отладку
Итак, в текущем коде видно значение входного параметра:
- data = "23 24 11 18" — строка с данными через пробел
- numsStringArr = — массив строк, который получился из входной переменной.
Если нажмем F10 2 раза, то окажемся на строке 11; во вкладках Watch и Variables и в самой странице с кодом увидим, что nums была инициализирована и в ней лежит массив целых чисел .
Если теперь нажмем F10 , то попадем внутрь цикла foreach и нажимая теперь F10 пока не окончится цикл, можно будет наблюдать на каждой итерации, как значение num и sum постоянно изменяются. num теперь можно будет наблюдать во вкладке Watch , потому что сейчас она доступна внутри цикла. Тем самым мы можем проследить шаг за шагом весь процесс изменения любых переменных и значений на любом этапе, который интересует.
Дальнейшие нажатия F10 переместит линию кода на строки 15, 16 и, наконец, 20.
Дополнительно
Если перейти во вкладку Breakpoints в нижней панели, то можно посмотреть все брейкпойнты, удалить все брейкпойнты, включить/выключить, а также еще более тонко настроить условие, при котором на данной отметке надо остановиться. В методе выше, например, нужно остановиться только когда sum превысит значение 20.
Это удобно, если останов нужен только при определённом значении, а не всегда (особенно в случае с циклами).
Производительность многопоточности: объяснить, почему получены такие результаты теста
Добрый день, при реализации кодирования Хэмминга) решил кодировать каждый блок данных в отдельном.
Вычислить значения выражений. Объяснить полученные результаты
Вычислить значения выражений. Объяснить полученные результаты. n---m m--<n n++>m Что не.
Решение
b типа int, а y - float
Кроме того, что само по себе выражение мягко говоря странное, если бы компилятор его и проглотил, в память, возможно занятую чем-то другим, вписался бы этот "результат".
Скорее всего есть ещё ошибки.
А чтобы их исправлять, нужно представлять себе, что программа делать должна. К примеру первая ошибка - округлить y, или тупо дробную часть приведением к int отбросить?
последняя строка в отрывке пытается произвести запись в невыделенную память *m, так как после операции m++ указатель m переводится на следующий объект после n типа double (а его там нет)
если целая часть float помещается в int, результат будет определённым
в арифметических операциях, если int и float, то они приводятся к float и результат тоже float
если целая часть float помещается в int, результат будет определённым
в арифметических операциях, если int и float, то они приводятся к float и результат тоже float
Ну это ошибка скорее логическая. Результат-то будет, но будет ли тем, который ожидалось получить?
к примеру:
так там ошибки нету, если числа маленькие (по модулю)
Так я ж не спорю. Формально можно так делать. Но как-то в книжках, которые читал, настоятельно рекомендуют более внимательно к приведению типов относиться.
нам препод сказал что при введени разных "б" должны выводиться разные значения адресов и т.п.
а тут все каждый раз одинаково
нам препод сказал что при введени разных "б" должны выводиться разные значения адресов и т.п.
а тут все каждый раз одинаково
С какого перепуга? При объявлении переменной (int b) резервируется 4 байта памяти. При изменении значения переменной меняется содержимое этих байтов. Но это всё те же 4 байта, находящиеся по адресу, выделенному при объявлении. Другая история с указателем (int *a). После инструкции a = &b в переменной a содержится адрес переменной b, а доступ к значению переменной b можно получить "разыменовыванием" указателя а (*a = 10; if ( b == 10) /* true */ ). Если присвоить самой переменной а какое-нибудь другое значение, к примеру a = NULL, ни на значение, ни на расположение в памяти переменной b это никак не повлияет.
Переменная errno и коды ошибок
errno – переменная, хранящая целочисленный код последней ошибки. В каждом потоке существует своя локальная версия errno, чем и обусловливается её безопасность в многопоточной среде. Обычно errno реализуется в виде макроса, разворачивающегося в вызов функции, возвращающей указатель на целочисленный буфер. При запуске программы значение errno равно нулю.
Стандарт ISO C определяет следующие коды:
- EDOM – (Error domain) ошибка области определения.
- EILSEQ – (Error invalid sequence) ошибочная последовательность байтов.
- ERANGE – (Error range) результат слишком велик.
Нехитрый скрипт печатает в консоль коды ошибок, их символические имена и описания:
Если вызов функции завершился ошибкой, то она устанавливает переменную errno в ненулевое значение. Если же вызов прошёл успешно, функция обычно не проверяет и не меняет переменную errno. Поэтому перед вызовом функции её нужно установить в 0 .
Как видите, описания ошибок в спецификации функции iconv() более информативны, чем в .
Функции работы с errno
Получив код ошибки, хочется сразу получить по нему её описание. К счастью, ISO C предлагает целый набор полезных функций.
void perror(const char *s);
strerror() не безопасная функция. Во-первых, возвращаемая ею строка не является константной. При этом она может храниться в статической или в динамической памяти в зависимости от реализации. В первом случае её изменение приведёт к ошибке времени выполнения. Во-вторых, если вы решите сохранить указатель на строку, и после вызовите функцию с новым кодом, все прежние указатели будут указывать уже на новую строку, ибо она использует один буфер для всех строк. В-третьих, её поведение в многопоточной среде не определено в стандарте. Впрочем, в QNX она объявлена как thread safe.
Поэтому в новом стандарте ISO C11 были предложены две очень полезные функции.
size_t strerrorlen_s(errno_t errnum);
Возвращает длину строки с описанием ошибки errnum .
errno_t strerror_s(char *buf, rsize_t buflen, errno_t errnum);
Копирует строку с описание ошибки errnum в буфер buf длиной buflen .
Функции входят в Annex K (Bounds-checking interfaces), вызвавший много споров. Он не обязателен к выполнению и целиком не реализован ни в одной из свободных библиотек. Open Watcom C/C++ (Windows), Slibc (GNU libc) и Safe C Library (POSIX), в последней, к сожалению, именно эти две функции не реализованы. Тем не менее, их можно найти в коммерческих средах разработки и системах реального времени, Embarcadero RAD Studio, INtime RTOS, QNX.
Стандарт POSIX.1-2008 определяет следующие функции:
char *strerror_l(int errnum, locale_t locale);
Возвращает строку, содержащую локализованное описание ошибки errnum , используя locale . Безопасна в многопоточной среде. Не реализована в Mac OS X, FreeBSD, NetBSD, OpenBSD, Solaris и прочих коммерческих UNIX. Реализована в Linux, MINIX 3 и Illumos (OpenSolaris).
int strerror_r(int errnum, char *buf, size_t buflen);
Копирует строку с описание ошибки errnum в буфер buf длиной buflen . Если buflen меньше длины строки, лишнее обрезается. Безопасна в многоготочной среде. Реализована во всех UNIX.
Увы, никакого аналога strerrorlen_s() в POSIX не определили, поэтому длину строки можно выяснить лишь экспериментальным путём. Обычно 300 символов хватает за глаза. GNU C Library в реализации strerror() использует буфер длиной в 1024 символа. Но мало ли, а вдруг?
Макрос assert()
Макрос, проверяющий условие expression (его результат должен быть числом) во время выполнения. Если условие не выполняется ( expression равно нулю), он печатает в stderr значения __FILE__ , __LINE__ , __func__ и expression в виде строки, после чего вызывает функцию abort() .
Если макрос NDEBUG определён перед включением , то assert() разворачивается в ((void) 0) и не делает ничего. Используется в отладочных целях.
Функции atexit(), exit() и abort()
int atexit(void (*func)(void));
Регистрирует функции, вызываемые при нормальном завершении работы программы в порядке, обратном их регистрации. Можно зарегистрировать до 32 функций.
_Noreturn void exit(int exit_code);
Главное преимущество exit() в том, что она позволяет завершить программу не только из main() , но и из любой вложенной функции. К примеру, если в глубоко вложенной функции выполнилось (или не выполнилось) некоторое условие, после чего дальнейшее выполнение программы теряет всякий смысл. Подобный приём (early exit) широко используется при написании демонов, системных утилит и парсеров. В интерактивных программах с бесконечным главным циклом exit() можно использовать для выхода из программы при выборе нужного пункта меню.
_Noreturn void abort(void);
Вызывает аварийное завершение программы, если сигнал не был перехвачен обработчиком сигналов. Временные файлы не уничтожаются, закрытие потоков определяется реализацией. Самое главное отличие вызовов abort() и exit(EXIT_FAILURE) в том, что первый посылает программе сигнал SIGABRT , его можно перехватить и произвести нужные действия перед завершением программы. Записывается дамп памяти программы (core dump file), если они разрешены. При запуске в отладчике он перехватывает сигнал SIGABRT и останавливает выполнение программы, что очень удобно в отладке.
Вывод в отладчике:
В случае критической ошибки нужно использовать функцию abort() . К примеру, если при выделении памяти или записи файла произошла ошибка. Любые дальнейшие действия могут усугубить ситуацию. Если завершить выполнение обычным способом, при котором производится сброс потоков ввода — вывода, можно потерять ещё неповрежденные данные и временные файлы, поэтому самым лучшим решением будет записать дамп и мгновенно завершить программу.
В случае же некритической ошибки, например, вы не смогли открыть файл, можно безопасно выйти через exit() .
Функции setjmp() и longjmp()
Вот мы и подошли к самому интересному – функциям нелокальных переходов. setjmp() и longjmp() работают по принципу goto, но в отличие от него позволяют перепрыгивать из одного места в другое в пределах всей программы, а не одной функции.
int setjmp(jmp_buf env);
Сохраняет информацию о контексте выполнения программы (регистры микропроцессора и прочее) в env . Возвращает 0 , если была вызвана напрямую или value , если из longjmp() .
void longjmp(jmp_buf env, int value);
Восстанавливает контекст выполнения программы из env , возвращает управление setjmp() и передаёт ей value .
Используя setjmp() и longjmp () можно реализовать механизм исключений. Во многих языках высокого уровня (например, в Perl) исключения реализованы через них.
Внимание! Функции setjmp() и longjmp () в первую очередь применяются в системном программировании, и их использование в клиентском коде не рекомендуется. Их применение ухудшает читаемость программы и может привести к непредсказуемым ошибкам. Например, что произойдёт, если вы прыгните не вверх по стеку – в вызывающую функцию, а в параллельную, уже завершившую выполнение?
Для опытных разработчиков информация статьи может быть очевидной и если вы себя таковым считаете, то лучше добавьте в комментариях полезных советов.
Как обнаружить ошибку
Прочитай информацию об исключении
Если выполнение программы прерывается исключением, то это первое место откуда стоит начинать поиск.
В каждом языке есть свои способы уведомления об исключениях. Например в JavaScript для обработки ошибок связанных с Web Api существует DOMException. Для пользовательских сценариев есть базовый тип Error. В обоих случаях в них содержится информация о наименовании и описании ошибки.
Каким бы языком вы не пользовались, не поленитесь изучить каким образом язык предоставляет информацию об исключениях и что эта информация означает.
Пример неявного переопределения параметров - использование интерцептора, который изменяет этот параметр в запросе и о котором вы не знаете.
Разверните стек
Загуглите текст ошибки
Очевидное правило, которым не все пользуются. Применимо к не типовым ошибкам, например связанным с конкретной библиотекой или со специфическим типом исключения. Поиск по тексту ошибки помогает найти аналогичные случаи, которые даже если не дадут конкретного решения, то помогут понять контекст её возникновения.
Прочитайте документацию
Если ошибка связана с использованием внешней библиотеки, убедитесь что понимаете как она работает и как правильно с ней взаимодействовать. Типичные ошибки, когда подключив новую библиотеку после прочтения Getting Started она не работает как ожидалось или выбрасывает исключение. Проблема может быть в том, что базовый шаблон подключения библиотеки не применим к текущему приложению и требуются дополнительные настройки или библиотека не совместима с текущим окружением. Разобраться в этом поможет прочтение документации.
Проведите исследовательское тестирование
Если используете библиотеку которая не работает как ожидалось, а нормальная документация отсутствует, то создайте тесты которые покроют интересующий функционал. В ассертах опишите ожидаемое поведение. Если тесты не проходят, то подбирая различные вариации входных данных выясните рабочую конфигурацию. Цель исследовательских тестов помочь разобраться без документации, какое ожидаемое поведение у изучаемой библиотеки в разных сценариях работы. Получив эти знания будет легче понять как правильно использовать библиотеку в проекте.
Бинарный поиск
Где обитают ошибки
Ошибки в своём коде
Ошибки в чужом коде
Если над проектом работает больше одного разработчика, чей код взаимодействует друг с другом, возможна ситуация, когда ошибка происходит в чужом коде. Может сложиться впечатление, что если программа раньше работала, а сломалась только после того, как вы добавили свой код, то проблема в этом коде. На деле может быть, что ваш код обращается к уже существующему чужому коду, но передаёт туда граничные значения данных, работу с которыми забыли протестировать и обработать такие случаи.
В зависимости от соглашений на проекте исправляйте такие ошибки как свои собственные, либо сообщайте о них автору и ждите внесения правок.
Ошибки в библиотеках
Ошибки могут падать во внешних библиотеках к которым нет доступа и в таком случае непонятно что делать. Такие ошибки можно разделить на два типа. Первый- это ошибки в коде библиотеки. Второй- это ошибки связанные с невалидными данными или окружением, которые приводят к внутреннему исключению.
Первый случай хотя и редкий, но не стоит о нём забывать. В этом случае можно откатиться на другую версию библиотеки и создать Issue с описанием проблемы. Если это open-source и нет времени ждать обновления, можно собрать свою версию исправив баг самостоятельно, с последующей заменой на официальную исправленную версию.
Во втором случае определите откуда из вашего кода пришли невалидные данные. Для этого смотрим стек выполнения и по цепочке прослеживаем место в котором библиотека вызывается из нашего кода. Далее с этого места начинаем анализ, как туда попали невалидные данные.
Ошибки не воспроизводимые локально
Ошибка воспроизводится на develop стенде или в production, но не воспроизводится локально. Такие ошибки сложнее отлавливать потому что не всегда есть возможность запустить дебаг на удалённой машине. Поэтому убеждаемся, что ваше окружение соответствует внешнему.
Проверьте версию приложения
На стенде и локально версии приложения должны совпадать. Возможно на стенде приложение развёрнуто из другой ветки.
Проверьте данные
Проблема может быть в невалидных данных, а локальная и тестовая база данных рассинхронизированы. В этом случае поиск ошибки воспроизводим локально подключившись к тестовой БД, либо сняв с неё актуальный дамп.
Проверьте соответствие окружений
Коварные ошибки
Похожая ситуация и с одинаково названными типами. Если сборка включает несколько проектов в которых присутствуют одинаково названные классы, то можно по ошибке обращаться не к тому который требуется. Чтобы избежать обоих случаев, убедитесь, что в месте возникновения ошибки идёт обращение к правильным типам и методам.
Дополнительные материалы
Алгоритм отладки
Проверь гипотезу - если гипотеза проверку не прошла то п.3.
Убедись что исправлено - если не исправлено, то п.3.
Подробнее ознакомиться с ним можно в докладе Сергея Щегриковича «Отладка как процесс».
Чем искать ошибки, лучше не допускать ошибки. Прочитайте статью «Качество вместо контроля качества», чтобы узнать как это делать.
Итого
При появлении ошибки в которой сложно разобраться сперва внимательно и вдумчиво читаем текст ошибки.
Смотрим стек выполнения и проверяем, не находится ли причина возникновения выше по стеку.
Если по прежнему непонятно, гуглим текст и ищем похожие случаи.
Если проблема при взаимодействии с внешней библиотекой, читаем документацию.
Если нет документации проводим исследовательское тестирование.
Если не удается локализовать причину ошибки, применяем метод Бинарного поиска.
-->
Задания Д24 C1 № 5372
Требовалось написать программу, при выполнении которой с клавиатуры считывается натуральное число N, не превосходящее 10 9 , и выводится количество цифр этого числа. Программист торопился и написал программу неправильно. (Ниже для Вашего удобства программа представлена на четырёх языках программирования.)
using namespace std;
cout « sum « endl;
Последовательно выполните следующее.
1. Напишите, что выведет эта программа при вводе числа 938.
2. Укажите одно число для которого эта программа будет работать верно.
3. Найдите все ошибки в этой программе (их может быть одна или несколько). Укажите все строки (одну или более), содержащие ошибки, и для каждой такой строки приведите правильный вариант. Обратите внимание, что требуется найти ошибки в имеющейся программе, а не написать свою, возможно, использующую другой алгоритм решения. Исправление ошибки должно затрагивать только строку, в которой находится ошибка.
1. Программа выведет число 4.
2. Программа работает верно для любого числа, начинающегося с 1, в том числе для 1.
(Достаточно указать любое такое число).
3. В качестве ответа для остальных чисел программа выдаёт число на 1 большее, чем нужно. Возможные варианты исправления для языка Паскаль:
1) исправление условия продолжения цикла на while (N > 9) do
2) исправление инициализации на sum := 0;, а условие цикла на while (N >= 1) do или while (N > 0) do. При этом замена на while (N >= 0) do корректной не является
3) исправление условия продолжения цикла на while (N >= 1) do или while (N > 0) do и вывод значения sum − 1.
Указания по оцениванию.
Обратите внимание! В задаче требовалось выполнить три действия. Баллы за данное задание начисляются как сумма баллов за верное выполнение каждого действия.
1. Верно указано, что именно выведет программа при указанных в условии входных данных.
2. Указано число, при котором программа работает верно.
3. Указаны все строки (одна или более), в которые нужно внести исправления, и эти исправлению внесены; при этом получена верно работающая программа.
При выполнении действия 3 верное указание на ошибку при неверном её исправлении не засчитывается.
Обратите внимание! Выбор ошибочных строк может быть выполнен не единственным способом.
В работе (во фрагментах программ) допускается наличие отдельных синтаксических ошибок, не искажающих замысла автора решения.
Читайте также: