ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ ВЫСОКОГО УРОВНЯ Раздел 2. Отладка компьютерных программ Старший преподаватель Кафедры ВС, к.т.н. Поляков Артем Юрьевич © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 1 Рассматриваемые вопросы • Жизненный цикл программы и место отладки в нем – Жизненный цикл программного обеспечения – Отладка программ • Инструменты отладки программ – утилиты сравнения исходного кода (diff) и системы управления версиями – сообщения компилятора – утилиты расширенной проверки синтаксиса и логики (lint) – программные отладчики – профилировщики © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 2 Жизненный цикл программного обеспечения • • • • • • • • Стандарты жизненного цикла ПО: ГОСТ 34.601-90, ISO/IEC 12207:1995 Формирование требований к АС Разработка концепции АС Техническое задание Эскизный проект Технический проект (отладка) Рабочая документация Ввод в действие Сопровождение АС. (отладка) © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 3 Отладка программ Макконнел, С. Совершенный код (Code complete) «Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете» • "Отладка — это процесс определения и устранения причин ошибок. Этим она отличается от тестирования, направленного на их обнаружение..." • "В некоторых проектах отладка занимает до 50% общего времени разработки…" • "Большинство дефектов " – это "недосмотры и опечатки" • В каждой группе есть … программист, бесконечно сражающийся с демоническими компьютерами, таинственными дефектами компилятора, скрытыми дефектами языка…" и "…заколдованным текстовым редактором…" • "... Отладка включает в себя поиск дефекта и его исправление. Поиск и понимание дефекта обычно составляют 90% работы..." © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 4 Научный метод отладки • Сбор данных при помощи повторяющихся экспериментов. • Формулирование гипотезы, объясняющей релевантные данные. • Разработка эксперимента, призванного подтвердить или опровергнуть гипотезу. • Подтверждение или опровержение гипотезы. • Повторение процесса в случае надобности. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 5 Метод поиска дефекта, основанный на научном методе 1. Стабилизация ошибки. 2. Определение источника ошибки. a. Сбор данных, приводящих к дефекту. b. Анализ собранных данных и формулирование гипотезы, объясняющей дефект. c. Определение способа подтверждения или опровержения гипотезы, основанного или на тестировании программы или на изучении кода. d. Подтверждение или опровержение гипотезы при помощи процедуры, определенной в п. 2 (с) 3. Исправление дефекта 4. Тестирование исправления • Поиск похожих ошибок © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 6 Инструменты отладки программ • утилиты сравнения исходного кода (diff) • системы управления версиями • сообщения компилятора • утилиты расширенной проверки синтаксиса и логики (lint) • программные отладчики • профилировщики © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 7 Утилиты сравнения исходного кода (diff) В вычислительной технике diff — утилита сравнения файлов, выводящая разницу между двумя файлами. Эта программа выводит построчно изменения, сделанные в файле (для текстовых файлов). Современные реализации поддерживают также двоичные файлы. Вывод утилиты называется «diff», или, что более распространено, патч, так как он может быть применён с программой patch. Вывод похожих утилит сравнения файлов также часто называется «diff». © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 8 Утилиты сравнения исходного кода (diff) (пример) #include <stdio.h> int main() { int x, y; if(y == 0) x = 10; } scanf("%d",&x); if( x > 5 ) x = 5; printf("x = %d", x); } #include <stdio.h> int main() { int x, y; if(y == 0){ x = 10; } scanf("%d",&x); if( x > 5 ) x = 5; printf("x = %d", x); } bad.c © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» good.c 9 Утилиты сравнения исходного кода (diff) (пример) $ diff –Naur bad.c good.c > good.patch --- bad.c 2011-10-20 08:36:32.000000000 +0700 +++ good.c 2011-10-20 12:10:56.403033996 +0700 @@ -4,7 +4,7 @@ { int x, y; + if(y == 0) if(y == 0){ x = 10; } scanf("%d",&x); © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 10 Системы управления версиями • Система управления версиями (от англ. Version Control System, VCS или Revision Control System) — программное обеспечение для облегчения работы с изменяющейся информацией. • Система управления версиями позволяет хранить несколько версий одного и того же документа, при необходимости, возвращаться к более ранним версиям, определять, кто и когда сделал то или иное изменение и многое другое. • Такие системы наиболее широко применяются при разработке программного обеспечения, для хранения исходных кодов разрабатываемой программы. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 11 Типичный порядок работы с системой управления версиями • Начало работы с проектом: извлечение рабочей копии проекта или той его части, с которой предстоит работать. • Ежедневный цикл работы – Обновление рабочей копии: по мере внесения изменений в проект другими разработчиками рабочая копия на компьютере разработчика стареет, расхождение её с основной версией проекта увеличивается. Обновление позволяет синхронизировать рабочую копию и текущую версию проекта. – Модификация проекта: разработчик модифицирует проект, изменяя входящие в него файлы в рабочей копии в соответствии с проектным заданием. Эта работа производится локально и не требует обращений к серверу VCS. – Фиксация изменений: Завершив очередной этап работы над заданием, разработчик фиксирует (commit) свои изменения, передавая их на сервер. Фиксации присваивается номер, по которому внесенные изменения будут доступны в дальнейшем. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 12 Существующие системы управлениями версиями • CVS (Concurrent Versions System, "Система Одновременных Версий") - одна из первых широко распространенных свободных централизованных систем управления версиями. • Subversion (также известная как "SVN") - свободная централизованная система управления версиями, официально выпущенная в 2004 году компанией CollabNet Inc. Subversion реализует все основные функции CVS и свободна от ряда недостатков последней. Используется многими сообществами разработчиков открытого программного обеспечения, в их числе: Apache, GCC, Python, Ruby, Mono, FreeBSD. • Git - свободная распределённая система управления версиями файлов. Проект был создан Линусом Торвальдсом для управления разработкой ядра Linux. Проекты, использующие Git: ядро и некот. дистрибутивы Linux, Drupal, GNU Core Utilities, Wine, Chromium, jQuery. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 13 Хостинг систем управления версиями • SourceForge (Subversion, Git, Mercurial, Bazaar, CVS) • Google Code (Subversion, Git и Mercurial) • GitHub (Git) • Codebase • Tigris.org © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 14 Компилятор • Компилятор – Программа, переводящая текст программы на языке высокого уровня в эквивалентную программу на машинном языке. • Компиляция – трансляция программы на язык, близкий к машинному, и последующая её компоновка. – трансляция программы, составленной на исходном языке, в объектный модуль (осуществляется компилятором) и последующая её компоновка в готовый к использованию программный модуль. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 15 Процесс преобразования исходных кодов в машинные кода из исходного prog1.c prog1.i prog1.o Редактирова ние связей (linking) program препроцессорная обработка progN.c компиляция и компоновка progN.i progN.o © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» (исполняемый (бинарный) файл) …. 03 30 01 03 20 02 01 03 10 02 02 03 02 40 …… 16 Советы по устранению синтаксических ошибок Внимательно читайте сообщения, выводимые компилятором! © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 17 Советы по устранению синтаксических ошибок #iclude <stdio.h> int main() { int x; ... $ gcc -o error1 error1.c error1.c:1: error: invalid preprocessing directive #iclude error1.c: In function ‘main’: error1.c: In function ‘main’: error1.c:6: warning: incompatible implicit declaration of built-in function ‘scanf’ error1.c:10: warning: incompatible implicit declaration of built-in function ‘printf’ © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 18 Советы по устранению синтаксических ошибок Не полагайтесь на номера строк в сообщениях компилятора. компилятор Си является однопроходным. Он читает программу сверху вниз и никогда не возвращается назад. Поэтому иногда он обнаруживает ошибку в строке, располагающейся после той, что действительно содержит ошибку. • Если компилятор сообщил о загадочной синтаксической ошибке, изучите фрагменты, расположенные прямо перед ошибкой. • Обнаружив истинный дефект попробуйте определить почему компилятор указал не на ту команду. Понимание особенностей компилятора поможет находить дефекты в будущем. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 19 Советы по устранению синтаксических ошибок Не полагайтесь на номера строк в сообщениях компилятора. #include <stdio.h> int main() { int x; scanf("%d",&x); if( x > 5 ) { x = 5; printf("x = %d", x); } $ gcc -o error1 error1_1.c error2.c: In function ‘main’: error2.c:11: error: expected declaration or statement at end of input © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 20 Советы по устранению синтаксических ошибок Не доверяйте второму сообщению об ошибке • Некоторые компиляторы обнаружив множественную ошибку выводят десятки бессмысленных сообщений о других ошибках © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 21 Советы по устранению синтаксических ошибок Не доверяйте второму сообщению об ошибке #include <stdio.h> int main() { int x, y; if(y == 0) x = 10; } scanf("%d",&x); if( x > 5 ) x = 5; printf("x = %d", x); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 22 Советы по устранению синтаксических ошибок Не доверяйте второму сообщению об ошибке $ gcc -o error2 error2.c error2.c:10: error: expected declaration specifiers or ‘...’ before string constant error2.c:10: error: expected declaration specifiers or ‘...’ before ‘&’ token error2.c:10: warning: data definition has no type or storage class error2.c:10: error: conflicting types for ‘scanf’ error2.c:10: note: a parameter list with an ellipsis can’t match an empty parameter name list declaration error2.c:11: error: expected identifier or ‘(’ before ‘if’ error2.c:15: error: expected declaration specifiers or ‘...’ before string constant error2.c:15: error: expected declaration specifiers or ‘...’ before ‘x’ error2.c:15: warning: data definition has no type or storage class error2.c:15: error: conflicting types for ‘printf’ error2.c:15: note: a parameter list with an ellipsis can’t match an empty parameter name list declaration error2.c:16: error: expected identifier or ‘(’ before ‘}’ token © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 23 Советы по устранению синтаксических ошибок • Разделение программы на части особенно эффективно #include <stdio.h> int main() при поиске синтаксических { ошибок. int x, y; • Если вы столкнулись с //if(y == 0) неуловимой синтаксической // x = 10; ошибкой — закомментируйте // } часть кода и scanf("%d",&x); перекомпилируйте if( x > 5 ) программу. x = 5; printf("x = %d", x); • Если ошибка исчезнет — ее нужно искать в этой части } кода © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 24 Отладка с помощью отладочных сообщений • Отладка программы требует полного понимания процессов, происходящих в процессе ее выполнения. • Одним из простейших в вместе с тем эффективных инструментов, которые позволяют отследить и лучше понять работу программы является журналирование программы. • Журналирование предполагает вывод на экран (или в файл) информации о событиях, возникающих в программе, и промежуточном состоянии программы, например: факт вызова функции и данные о ее аргументах, содержимое переменных и т.п. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 25 Использование отладочных сообщений • Отладочные сообщения должны находиться в ключевых узлах программы и позволять отследить ход ее выполнения. • В случае поиска дефекта необходимо размещение дополнительных выводов, позволяющих детализировать информацию о потенциально дефектных фрагментах кода. • Необходимо соблюдать баланс между количеством выводимой информации и ее качеством, так как при визуальном осмотре важная информация может "утонуть" в потоке ненужных сообщений. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 26 Технические аспекты, связанные с отладочными сообщениями • Отладочные сообщения могут сами по себе содержать ошибки. • Вывод на экран буферизируется построчно, т.е. сообщения лежат в буферах до тех пор, пока не обнаружен символ '\n' (Enter – перевод каретки). • В случае фатальной ошибки, приводящей к аварийному завершению программы (наиболее распространена ошибка сегментации (Segmentation Fault) отладочные сообщения, находящиеся в буферах, не будут выведены. • Часто бывает удобно иметь возможность оперативно отключать и включать отладочные выводы. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 27 Эффективная реализация журналирования debug.h #ifndef DEBUG_H #define DEBUG_H #include<stdio.h> #define PDEBUG(lev,fmt,args...) #ifdef DEBUG #undef PDEBUG #define PDEBUG(lev,fmt,args...) \ if( lev<=DEBUG ) \ printf("%s: %d: " fmt " \n",__FUNCTION__, __LINE__, ## args ) #endif #endif © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 28 Применение журналирования #include <stdio.h> #define DEBUG 100 #include "debug.h" int main() { int i = 0; $ ./debug main: 11: main: 11: main: 11: main: 11: main: 11: i i i i i = = = = = 0 1 2 3 4 while( i < 5 ){ PDEBUG(1, "i = %d", i); i++; } } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 29 Применение журналирования #include <stdio.h> $ ./debug #define DEBUG 0 #include "debug.h" int main() { int i = 0; while( i < 5 ){ PDEBUG(1, "i = %d", i); i++; } } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 30 Пример отладки #include <stdio.h> int main() { int i, x, sum; scanf("%d",&x); while( i<x ) { sum += i; } printf("sum=%d\n",sum); } $ ./sum_ex 10 sum=-1216214131 $ ./sum_ex 20 sum=-1216758754 $ ./sum_ex 30 sum=-1217401581 После перекомпиляции постоянное зависание. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 31 Журналирование. Шаг №1. #include <stdio.h> #define DEBUG 100 #include "debug.h" int main() { int i, x, sum; printf("Enter x: "); scanf("%d",&x); PDEBUG(1,"sum = %d\n",sum); while( i<x ){ PDEBUG(1,"sum = %d\n",sum); sum += i; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 32 Журналирование. Шаг №1. (результаты) Enter main: main: main: main: main: main: main: main: main: x: 10 9: sum = -1216570528 11: sum = -1216570528 11: sum = 1861651284 11: sum = 644905800 11: sum = -571839684 11: sum = -1788585168 11: sum = 1289636644 11: sum = 72891160 11: sum = -1143854324 . . . <бесконечно> Ctrl + C – принудительное завершение программы Вывод: необходима инициализация sum © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 33 Журналирование. Шаг №2. #include <stdio.h> #define DEBUG 100 #include "debug.h" int main() { int i, x, sum = 0; printf("Enter x: "); scanf("%d",&x); PDEBUG(1,"sum = %d\n",sum); while( i<x ){ PDEBUG(1,"sum = %d\n",sum); sum += i; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 34 Журналирование. Шаг №2. (результаты) $ ./sum_ex_dbg_1 Enter x: 10 main: 9: sum = 0 main: 11: sum = 0 main: 11: sum = -1216471052 main: 11: sum = 1862025192 main: 11: sum = 645554140 main: 11: sum = -570916912 . . . <бесконечно> Ctrl + C – принудительное завершение программы Вывод: необходимо добавить проверку счетчика i © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 35 Журналирование. Шаг №3. #include <stdio.h> #define DEBUG 100 #include "debug.h" int main() { int i, x, sum = 0; printf("Enter x: "); scanf("%d",&x); PDEBUG(1,"sum = %d, i=%d\n",sum,i); while( i<x ){ PDEBUG(1,"sum = %d, i=%d\n",sum,i); sum += i; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 36 Журналирование. Шаг №3. (результаты) $ ./sum_ex_dbg_2 Enter x: 10 main: 9: sum = 0 main: 11: sum = 0, i = -1217404940 main: 11: sum = -1217404940, i = -1217404940 main: 11: sum = 1860157416, i = -1217404940 main: 11: sum = 642752476, i = -1217404940 main: 11: sum = -574652464, i = -1217404940 main: 11: sum = -1792057404, i = -1217404940 . . . <бесконечно> Ctrl + C – принудительное завершение программы Вывод: необходима инициализация счетчика i © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 37 Журналирование. Шаг №4. #include <stdio.h> #define DEBUG 100 #include "debug.h" int main() { int i = 0, x, sum = 0; printf("Enter x: "); scanf("%d",&x); PDEBUG(1,"sum = %d, i=%d\n",sum,i); while( i<x ){ PDEBUG(1,"sum = %d, i=%d\n",sum,i); sum += i; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 38 Журналирование. Шаг №4. (результаты) $ ./sum_ex_dbg_3 Enter x: 10 sum = 0 sum = 0, i = 0 sum = 0, i = 0 sum = 0, i = 0 sum = 0, i = 0 sum = 0, i = 0 sum = 0, i = 0 sum = 0, i = 0 . . . <бесконечно> Ctrl + C – принудительное завершение программы Вывод: счетчик не изменяется. Добавить инкремент. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 39 Журналирование. Шаг №5. #include <stdio.h> #define DEBUG 100 #include "debug.h" int main() { int i = 0, x, sum = 0; printf("Enter x: "); scanf("%d",&x); PDEBUG(1,"sum = %d, i=%d\n",sum,i); while( i<x ){ PDEBUG(1,"sum = %d, i=%d\n",sum,i); sum += i; i++; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 40 Журналирование. Шаг №5. (результаты) $ ./sum_ex_dbg_4 Enter x: 10 sum = 0 sum = 0, i = 0 sum = 0, i = 1 sum = 1, i = 2 sum = 3, i = 3 sum = 6, i = 4 sum = 10, i = 5 sum = 15, i = 6 sum = 21, i = 7 sum = 28, i = 8 sum = 36, i = 9 sum=45 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 41 Журналирование. Шаг №6. (отключение отладки) #include <stdio.h> #define DEBUG 0 #include "debug.h" int main() { int i = 0, x, sum = 0; printf("Enter x: "); scanf("%d",&x); PDEBUG(1,"sum = %d, i=%d\n",sum,i); while( i<x ){ PDEBUG(1,"sum = %d, i=%d\n",sum,i); sum += i; i++; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 42 Журналирование. Шаг №6. (результаты) $ ./sum_ex_fix1 Enter x: 10 sum=45 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 43 Программные отладчики Современные отладчики совершенствуются. Они позволяют: непрерывно • прерывать выполнение программы на некоторой строке или при достижении конкретной строки n раз, а также при изменении некоторой переменной или присвоении ей конкретного значения; • возобновлять выполнение программы; • производить трассировку программы, т.е. выполнять ее пошагово; © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 44 Программные отладчики (2) • выполнять исследование данных, в частности анализировать значения переменных в любой момент выполнения программы; • изменять данные, в частности значения переменных; • отображать ход выполнения программы с привязкой к исходным кодам; • просматривать цепочку содержимое стека вызовов; вызовов функций, т.е. • подключаться к выполняющейся программе; © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 45 Пример отладки #include <stdio.h> int main() { int i, x, sum; scanf("%d",&x); while( i<x ) { sum += i; } printf("sum=%d\n",sum); } $ ./sum_ex 10 sum=-1216214131 $ ./sum_ex 20 sum=-1216758754 $ ./sum_ex 30 sum=-1217401581 После перекомпиляции постоянное зависание. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 46 Применение отладчика. Шаг №1. Компиляция с флагом "-g" $ gcc -o sum_ex sum_ex.c –g Запуск отладчика $ gdb sum_ex GNU gdb (GDB) 7.2-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from sum_ex...done. (gdb) © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 47 Применение отладчика. Шаг №2. Пробный запуск программы (команда "r"): (gdb) r Starting program: sum_ex Enter x: 10 . . . <бесконечно> Ctrl + C – принудительное завершение программы Program received signal SIGINT, Interrupt. main () at sum_ex.c:10 10 while( i<x ) (gdb) © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 48 Применение отладчика. Шаг №3. Проверка значений x и i . . . 10 while( i<x ) (gdb) inspect i $1 = -1208197132 (gdb) inspect x $2 = 10 . . . ВЫВОД: переменная i не инициализирована! © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 49 Применение отладчика. Шаг №4. Просмотр исходного кода программы в отладчике . . . (gdb) l main 1 #include <stdio.h> 2 3 int main() 4 { 5 int i, x, sum; 6 7 printf("Enter x: "); 8 scanf("%d",&x); 9 10 while( i<x ) . . . © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 50 Применение отладчика. Шаг №5. Установка точки останова на функции main для пошаговой отладки (gdb) b main Breakpoint 1 at 0x804841d: file sum_ex.c, line 7. Перезапуск программы заново (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: sum_ex Breakpoint 1, main () at sum_ex.c:7 7 printf("Enter x: "); © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 51 Применение отладчика. Шаг №6. Инициализация переменной i (gdb) inspect i $3 = -1208197132 (gdb) set var i = 0 (gdb) inspect i $4 = 0 Продолжение работы программы: (gdb) c Continuing. Enter x: 10 … < программа зависла > © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 52 Применение отладчика. Шаг №7. Ctrl + C – принудительное завершение программы Program received signal SIGINT, Interrupt. main () at sum_ex.c:10 10 while( i<x ) (gdb) inspect i $5 = 0 ВЫВОД: переменная i не изменила своего значения! © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 53 Применение отладчика. Шаг №8. Проверка есть ли инкремент i в цикле (gdb) l main 1 #include <stdio.h> 2 3 int main() . . . 9 10 while( i<x ) (gdb) l 11 { 12 sum += i; 13 } 14 printf("sum=%d\n",sum); 15 16 } (gdb) © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 54 Применение отладчика. Шаг №9. Инкремент отсутствует. Требуется модифицировать программу, добавив в нее инициализацию i и ее инкремент в теле цикла. Для этого необходимо завершить отладочную сессию (gdb) q A debugging session is active. Inferior 1 [process 15015] will be killed. Quit anyway? (y or n) y © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 55 Применение отладчика. Шаг №10. Измененная программа #include <stdio.h> int main() { int i = 0, x, sum; printf("Enter x: "); scanf("%d",&x); while( i<x ){ sum += i; i++; } printf("sum=%d\n",sum); } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 56 Применение отладчика. Шаг №11. Компиляция программы и ее запуск gcc -o sum_ex_gdb1 sum_ex_gdb1.c -g $ ./sum_ex_gdb1 Enter x: 10 sum=-1216803955 ВЫВОД: Программа больше не зацикливается, однако результат очевидно неправильный. Требуется продолжить отладку. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 57 Применение отладчика. Шаг №12. Запуск отладчика $ gdb sum_ex_gdb1 . . . (gdb) b main Breakpoint 1 at 0x804841d: file sum_ex_gdb1.c, line 5. (gdb) r Starting program: sum_ex_gdb1 Breakpoint 1, main () at sum_ex_gdb1.c:5 5 int i = 0, x, sum; (gdb) n 7 printf("Enter x: "); (gdb) n 8 scanf("%d",&x); © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 58 Применение отладчика. Шаг №12. (продолжение) . . . 7 printf("Enter x: "); (gdb) n 8 scanf("%d",&x); (gdb) n Enter x: 5 10 while( i<x ) (gdb) n 12 sum += i; (gdb) inspect sum $1 = -1208022176 ВЫВОД: Переменная sum не инициализирована © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 59 Критика программных отладчиков "Интерактивный отладчик — великолепный пример инструмента, который не нужен: он поощряет хакерство методом проб и ошибок, а не систематичное проектирование и позволяет непрофессионалам скрыть свою некомпетентность." Харлан Миллз, IBM research fellow and chief programmer «... I don't like debuggers. Never have, probably never will. I use gdb all the time, but I tend to use it not as a debugger, but as a disassembler on steroids that you can program... I happen to believe that not having a kernel debugger forces people to think about their problem on a different level than with a debugger... Without a debugger, you tend to think about problems another way...» Линус Торвальдс, создатель ядра Linux © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 60 Критика программных отладчиков (2) " … Наш личный выбор — стараться не использовать отладчики, кроме как для просмотра стека вызовов или же значений пары переменных …. очень легко потеряться в деталях сложных структур данных и путей исполнения программы; мы считаем пошаговый проход по программе менее продуктивным, чем усиленные размышления и код, проверяющий сам себя в критических точках… Слепое блуждание в отладчике, скорее всего, непродуктивно. Полезнее использовать отладчик, чтобы выяснить состояние программы, в котором она совершает ошибку, а затем подумать о том, как такая ошибка могла возникнуть. ..." Керниган, Б., Пайк Р. Практика программирования © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 61 В защиту программных отладчиков "… Любой эффективный инструмент можно использовать правильно и неправильно. Отладчик — не исключение... Отладчик не заменит грамотного рассуждения. Но иногда никакие мысли не заменят хороший отладчик. Наиболее эффективная комбинация — ясный ум и хороший отладчик ..." Макконнел, С. Совершенный код. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 62 Стандарт оформления кода (Code convention) Стандарт оформления кода (стандарт кодирования, стиль программирования) (англ. coding standards, coding convention или programming style) – набор правил и соглашений, используемых при написании исходного кода на некотором языке программирования. Наличие общего стиля программирования: • позволяет избежать синтаксических и смысловых ошибок; • упрощает поиск синтаксических ошибок; • облегчает понимание и поддержание исходного кода, написанного больше чем одним программистом. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 63 Существующие стандарты оформления кода на языке Си • • • • • • C Programming language (K&R) [5] Linux Kernel Coding Style [6] GNU Coding Standards [7] Ganssle Group's Firmware Development Standard Netrino Embedded C Coding Standard Micrium C Coding Standard • … © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 64 Соглашение об оформлении кода на языке Си, разрабатываемого в рамках курсов "Программирование" и "Языки программирования" • • • • Отступы Расположение фигурных скобок Имена переменных Комментарии © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 65 Отступы (http://en.wikipedia.org/wiki/Indent_style) • используются для форматирования исходного кода программы • применяются для улучшения читабельности. • предназначены только для программистов • компилятор языка Си игнорирует такие разделители, как пробел (Space), табуляция (Tab) и перевод на новую строку (Enter). © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 66 Отступы (правила использования) • Для формирования табуляция (клавиша Tab) отступов используется • Каждый новый блок операторов {…} увеличивает количество отступов на один: for ( i=0 ; i < 10; i++ ){ x = i*i; if( x > 10 ){ z = z + 1; ... } y = x – 1; } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 67 Отступы (пример) int main() { ... while (x < y) { x *= 2; f = func(x); } if (f > 0){ count++; printf("f(%d) = %f\n", x, f); } else count--; printf("count=%d\n", count); ... } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 68 Расположение фигурных скобок (http://en.wikipedia.org/wiki/Indent_style) • Открывающаяся фигурная скобка располагается на одной строке с циклическим оператором или оператором ветвления. Закрывающаяся – на новой строке: for ( i=0 ; i < 10; i++ ){ ... } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 69 Расположение фигурных скобок (Исключение 1) • Для функций открывающаяся фигурная скобка располагается на новой строке: int main() { ... } © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 70 Расположение фигурных скобок (Исключение 2) • Для циклов с постусловием и для конструкции ветвления с оператором else закрывающаяся фигурная скобка располагается не в новой строке: if( условие ){ ... } else do { ... } while( условие ); © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 71 Имена переменных • Имена переменных предназначение должны отражать их • Длина имени переменной должна быть не более 15-20 символов для сохранения читабельности кода. • Для переменных со сложным именем предпочтительным является вариант first_left_counter, а не FirstLeftCounter. • Для счетчиков в циклах допускается применять простые имена, например i, j. © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 72 Комментарии • Комментарии являются признаком хорошего тона при написании программ. Однако существует опасность создания слишком подробных комментариев. Поэтому желательно придерживаться следующих правил: – не следует объяснять КАК работает код, необходимо пояснить ЧТО (какую функцию) он должен делать; – аккуратно написанный код говорит сам за себя, с другой стороны не стоит тратить время на то чтобы объяснить плохо написанный код; © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 73 ИТОГ • Описан жизненный цикл программы и место отладки в нем. • Рассмотрено применение существующих инструментов отладки программ, в частности: – утилиты сравнения исходного кода diff; – систем управления версиями; – сообщений компилятора GCC; – программного отладчика GDB. • Представлено соглашение об оформлении кода на языке Си, разрабатываемого в рамках курсов "Программирование" и "Языки программирования". © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 74 Литература 1) Керниган, Б., Пайк Р. Практика программирования, Вильямс, 2004, 288 с. 2) Макконнел, С. Совершенный код. Мастер класс / Пер. с англ. – М.:Издательско-торговый дом "Русская редакция"; СПб.:Питер, 2005. – 896 с.: ил. ISBN 5-7502-0064-7, 5-46900822-3. 3) http://ru.wikipedia.org/wiki/GNU_Debugger 4) http://www.gnu.org/software/gdb/ 5) Kernigan, B.W., Ritchie D.M. The C programming Language // Prentice-Hall, 1988. – ISBN 0-13-110362-8 6) Torvalds L. Linux Kernel Coding Style // https://computing.llnl.gov/linux/slurm/coding_style.pdf 7) GNU Coding Standards // http://www.gnu.org/prep/ /standards/standards.html#Syntactic-Conventions © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» 75