Искусство программирования на Си (984073), страница 23
Текст из файла (страница 23)
С-программисты любят указатели потому, недостаточно тщательно продумали условия окончания из многих найденных мною ошибок. Исправление этой после освобождения области памяти, на которую он что они так просты и, несмотря на это, так могуществен- выделенной области. Тот, кто не разбирается в указа- одной было наибольшим достижением уже лота бы указывает. Стандарт АХЯ гарантирует, что обращение ны. Те, кто чувствует отвращение к указателям, дума- гелях, может, конечно, столкнуться с проблемами бопотому, что оператор нс мог запустить пакет опять посЕгее ют, что они излишне усложнены. Одно можно сказать лес или менее в любое время. ле выполнения этой программы. определенно: ошибки, связанные с указателями, труд- Рассмотрим обычную ошибку новичка, прсдставлен- Неверное представление, приводящее к этой ошиб- вполне безврелно. Поэтому, установив указатели в нее всего обнаружить и исправить.
ную в листинге 7.4, где программист использует память, ке, имеет такой смысл: "Я только что освободил эту )Ч1)ЕЕ, мы прелотврашаем повреждение, которое может Если мы имеем представление об указателях, то, для него не отведенную. область памяти. 3аписанные в ней величины данных не произойти при послед) юшей передаче их фУнкции скорее всего, столкнемся с проблемой указателей, если могли измениться за те несколько микросекунд, кото- ГгееО.
Это полезная предосторожность, но вы можете рые прошли до моего обращения к ним, поэтому все посчитать ее слиизком слабой формой защиты. Вторая Листинг 7.4. Ошибка распределения памяти. должно быть в порядке. Сейчас они мне по-прежнему стратегия подразумевает более надежный подход, но он Етпс1иди <аеа1п.Ь> доступны". Эзо неверно. Когда вы освобождаете память, лолжен быть введен на стадии разработки программы. Фхпс1иае <веа11Ь.Ь> это происходит навсегда, начиная именно с данного ПРогРамма лолжна освобожлать память в нужном месмомснта. Если вы хоппе снова обратиться к этим дан- те, и это нузкное место должно быть только одно. 1пе па1п(ип14] ным, не освобождайте прежде занимаемую ими память.
В илсале модуль, в котором память распределяется, дол( сЬаг нЕ11епппи. Если для какой-либо другой цели вам снова нузхна па- жен отвечать и за ее освобождение. Ртгк *Ер мять вызовите функцию пыйос() снова. Менее опасной, чем рассмотренные, но все-таки хпе Кевиы = ЕХгт ВВССЕВВ; Другая ошибка, связанная с указателями, закзюча- сУШествснной ЯатЯетсЯ проблема так называемой Утсчется в неоднократном освобождении памяти. Приходят ки памяти. Это звучит как серьезная аппаратная пробле- рг1пСЕ( тати ргиягап са1си1аеив регтисе аяиагеа.1п ); на ум две стратегии предотвращения этой ошибки.
Пер ма, но на самом деле это просто небрежность в освобож- Ианраааенае кода нраграам Глава 7 кС Веан11 -1 ) гееигв веаи11; ) ница с указателями и массивами может стать причиной ошибок. Например: 1пс Оесьод1нпонгаргоаазнег (еьак *Ьияяьк) ( 1ве аева11 = 0 > 11((деев(Ьвгтег, а1хеог Ьатгег, асбьв) ипьь) ( Сложность этой оц|ибки заключается в том, что все выглядит корректно. Конструкция вызова (йеш булет прекрасно работать, только если Ьв(тег — это массив сЬаг.
Это не так. Наиболес длинное имя пользователя, которое можно прочесть таким способом, имеет Игео((сЬаг*) -1 байтов. Что произойдет, если изменить параметр следующим образом? нис. Чаше всего неправильное указание имеет место в группах связных структур с динамическим выделением памяти (связные списки н все родственные им структуры). В холе дальнейшего обсуждения я буду говорить о связных списках, но основные принципы применимы к любым подобным структурам данных, таким как очереди, стеки и деревья. Проблемы возникают, котла программист неверно учитывает все возможные перестановки, когла добавляет новый элемент в список (или другую структуру данных), уничтожает элемент, ищет элемент или перемешает сто.
Рассмотрим перестановку двух элементов в двунаправленном связном списке. Ваша стратегия перестановки должна принимать во внимание каждую из следуюгцих возможностей: один из элементов находится в конце списка; оба элемента находятся в одном или в другом конце списка; элементы расположены рядом; элементы не расположены рядом; элементы расположены через один элемент. Программные средства отладки Ссзи бы только сушествовата программа, которая могла бы отладить вашу программу! Но такой программы нет (по крайней мере, пока еше нет).
Лучшее, чем мы сейчас располагаем, — зто программа, собирающая информацию, которая поможет вам отладить свою прог)закгму. Программные средства отладки так называются в основном ради красного слонца: они нс выполняют никакой отладки, ее делаете вы. Несмотря на это, средства отладки действительно чрезвычайно полезны, они дают вам верную информацию для определенна проблемы (если только вы можете определить, какая именно информация вам нужна.) Существует два типа программных средств: те, что пишете вы, и те, которые написаны другими. Это относится и к средствам отладки. Рассмотрим оба этих типа. Коммерческие отладчики программ реального времени, имеющих дело с событиями в регшьном масштабе времени. Например, рассмотрим программу, посылающую пакеты на асинхронный ТСР/)Р-сокег. Вы нс знаете и пытастссь выяснить, почему программа посылает слишком быстро слишком много паке~он по низкоскоростному каналу связи.
Пошаговое выполнение вашего исходного кода — медленный процесс, даже если вы делаете это так быстро, как только можете, по сравнению со скоростью выполнения программы вне отладчика. Поэтому при пошаговом выполнении этого ТСР/)Р-кода все работает прекрасно. Когда программа выполняется в нормальном режиме, вообще ничего нс работает. В данной ситуации необходима внутренняя информация о состоянии программы, когда она выполняется с реальной скоростью вне отладчика. В подобных ситуациях мы возвращаемся к трассировочным кодам.
Что нам нужно от трассировочного кала" .Мы определенно не хотим включать трассировочный код в ко- ";г,.",«;,: Шг;„,' ~";„,"»:,", э:Лтр аг.ат ФГ) )ге ~П Пересматреннмйяямк С часть ) перемснныс, также рекомендуется ограничиться малым числом функций. Ниже представлен набор трассировочных макросов и функций, удовлетворяюший всем выше перечисленным требованиям (он есть и на ФеЬ-сайте издательства "ДиаСофт" ). Я знаю, что это надежный и испытанный набор, поскольку сам использовал его в нескольких проектах. Я постарался исключить все ошибки.
Может быть он даже вообще не содерхсит ошибок! (Если там ошибка все-таки есть, то, скорее всего, в коде, работаюшем с датой. Перефразируя Петера ван дер Линдена (Регег чап бег Е[пс[еп), можно сказать: тот, кто думает, что программная обработка лат проста, никогда этого не делал.) Для этой книги я переименовал этот набор в Сройои (беглый поиск по %еЬ-страницам позволил исключить более очевидные названия, поскольку они уже использованы). Сначала — заголовок в листинге 7.6.
Ь|ВВ О,~ Фпчаг =[вп]',1 пчаг) / ° В1 дпя ввогс 1пс */ ]Ое[ьпе Нт ВОЬЬОВ[азчаг) Стопов[ ВЬЬВ Ь1ВВ, 1 Ос] )аьчаг =[ВВО], 1 а1чаг) /* НО для авпгс Опазвпеа гас*/ ]аегьпе НО ВОЬЬОВ(апчаг) Сро11оч[ В1ЬВ е 1 Ь1ВВ,1 0,1 Фвпчаг =[Ъвп] ,1 апчаг) /' Ы дпя Ьопв 1пе */ ]Оп([ Ы ВОЬЬОВ(1 г) Стонов( т)ЬВ ЫВВ 0,1 )1чаг =($16) , 1 1чаг) Листинг 7.6. Заголовок трассировочных макросов. /* ЬО для Ьопв Опаьвпее 1пг */ Ислрааленне када лраграмм Глава 7 Иго роаеенне кода программ Пересиоогренныи кзык Г Часть 1 Глава 7 а, Ь) $0ег1пе СР РОИСООТ(гопспане, Гуреарес, гча1) $аегтпе Р РОЬЬОНЗ(гогнас, а, Ь, с) СтоЫон( ГГЬЕ йде11ПЕ СГОЬЬОН СЬОЕЕООНИ 11ЫЕ 0,1 йепд1$ /* Трассвровочные операторы включены влв выключены */ Гогыас, 1 а, Ь, с! йепа1$ /* сРОььон в е/ $аегтпе Р РОЬЬОН(((огвас, а, Ь, с, О) Сго11оы( Г1ЬЕ ьтые, 1 А теперь — исходный код в листинге 7.7.
0,1 Листинг 7.7. КОД трассировочного макроса. $(пс1пде <вс01о.й> Ие(1пе Р РОЬЬОИ5(гогвас, а, Ь, с, а, е) $гпс1оде <асдагв й> Стонов( ГЬЬЕ $(пс1пае <Г1ве.п> ЫИЕ, 1 йгпс1ппе <вгггп9 В> 0,1 Гогваг, 1 $1пс1пде сго11оы. й" а, Ь, с, д, е) /е $дес(ое СР СООЕ(соде) соде СессосрпСР11енаве()е $аеггпе СР РОЫС1И(гппспаве) Сго11он( Р1ЬЕ , 1 /* Эта фувкцыя создает вмя файла для ыспользоваывя фувкцвей Горев.