Глава 12 Общие библиотечные интерфейсы – часть 2 — КиберПедия 

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим...

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

Глава 12 Общие библиотечные интерфейсы – часть 2

2021-01-29 81
Глава 12 Общие библиотечные интерфейсы – часть 2 0.00 из 5.00 0 оценок
Заказать работу

 

В главе 6, «Общие библиотечные интерфейсы – часть 1», был представлен первый набор API библиотеки общего пользования. В некотором смысле, эти API поддерживают работу с фундаментальными объектами, которыми управляют системы Linux и Unix: время дня, пользователи и группы для файлов, сортировка и поиск.

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

 

Операторы проверки:

 

Оператор проверки (assertion) является утверждением, которое вы делаете о состоянии своей программы в определенный момент времени ее исполнения. Использование операторов проверок для программирования было первоначально разработано Хоаром (C.A.R. Hoare)[121]. Общая идея является частью «верификации программы»: так же, как вы проектируете и разрабатываете программу, вы можете показать, что она правильна, делая тщательно аргументированные утверждения о проявлениях кода вашей программы. Часто такие утверждения делаются об инвариантах – фактах о состоянии программы, которые, как предполагается, остаются верными на протяжении исполнения куска программы.

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

 

 

Этот пример определяет условия, используя комментарии. Но не было бы лучше проверить условия с использованием кода? Это является задачей макроса:

 

 

скалярное выражение

Когда скалярное выражение ложно, макрос выводит диагностическое сообщение и завершает программу (с помощью функции; см. раздел 12.4 «Совершение самоубийства:»). снова предоставляет функцию, на этот раз с оператором проверки и функцией:

 

 

После компиляции и запуска оператор проверки в строке 12 «выстреливает»:

ch12‑assert

 

 

Сообщение от варьирует от системы к системе. Для GLIBC на GNU/Linux сообщение включает имя программы, имя файла с исходным кодом и номер строки, имя функции, а затем текст завершившегося неудачей условия. (В этом случае именованная константа проявляется в виде своего макрорасширения '.)

Сообщение '' означает, что создала файл; т.е. снимок адресного пространства процесса непосредственно перед его завершением.[122] Этот файл может быть использован впоследствии с отладчиком; см. раздел 15.3 «Основы GDB». Создание файла является намеренным побочным результатом; предполагается, что произошла решительная ошибка, и вы хотите исследовать процесс с помощью отладчика для ее определения.

Вы можете отменить оператор проверки, компилируя свою программу с помощью опции командной строки ''. Когда этот макрос определен до включения, макрос расширяется в код, который ничего не делает. Например:

gcc ‑DNDEBUG=1 ch12‑assert.c ‑о ch12‑assert

ch12‑assert

 

Здесь мы получили настоящий дамп ядра! Мы знаем, что операторы проверки были запрещены; сообщения «failed assertion» нет. Что же случилось? Рассмотрите строку 15 при вызове из строки 37. В этом случае переменная равна. Доступ к памяти через указатель является ошибкой. (Технически различные стандарты оставляют «неопределенным» то, что происходит при разыменовывании указателя. Наиболее современные системы делают то же, что и GNU/Linux; они завершают процесс, посылая ему сигнал; это, в свою очередь, создает дамп ядра. Этот процесс описан в главе 10 «Сигналы».

Этот случай поднимает важный момент относительно операторов проверки. Часто программисты ошибочно используют операторы проверки вместо проверки ошибок времени исполнения. В нашем случае тест '' должен был быть проверкой времени исполнения:

 

Тест '' (строка 13) менее проблематичен; если равен 0 или меньше 0, цикл никогда не исполнится, и (правильно) возвратит ‑1. (По правде, этот оператор проверки не нужен, поскольку код правильно обрабатывает случай ''.)

Логика, стоящая за отменой оператора проверки, заключается в том, что дополнительные проверки могут снизить производительность программы и поэтому должны быть запрещены в заключительной версии программы. Хоар[123], однако, сделал такое замечание:

 

«В конце концов, абсурдно делать тщательные проверки безопасности при отладочных запусках, когда к результатам нет никакого доверия, а затем удалять их из финальных версий, когда ошибочный результат может быть дорогим или катастрофическим. Что бы мы подумали об энтузиасте‑мореплавателе, который надевает свой спасательный жилет при тренировке на сухой земле и снимает его, как только выходит в море?»

 

С такими мнениями, наша рекомендация заключается во внимательном использовании операторов проверки‑ во‑первых, для любого данного оператора проверки рассмотрите возможность использования вместо него проверки времени исполнения. Во‑вторых, тщательно разместите свой оператор проверки, чтобы не было возражений против их оставления на своем месте лаже в финальной версии вашей программы.

Наконец, отметим следующее из раздела «Ошибки» справочной страницы GNU/Linux assert (3):

 

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

 

Знаменитый принцип неопределенности Гейзенберга из физики указывает, что чем более точно вы определите скорость частицы, тем менее точно вы определите ее положение, и наоборот. В терминах непрофессионала это означает что простой факт наблюдения частицы влияет на нее.

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

Справочная страница предостерегает нас от использования при вызовах выражений с побочными эффектами:

 

Здесь побочным эффектом является увеличение указателя p как часть теста. Когда определен, аргумент выражения исчезает из исходного кода; он никогда не исполняется. Это может привести к неожиданной неудаче. Однако, как только при подготовке к отладке запрет на операторы проверки отменяется, все начинает снова работать! Такие проблемы трудно отследить.

 

12.2. Низкоуровневая память: функции XXX

 

Несколько функций предоставляют возможность для работы с произвольными блоками памяти низкоуровневые службы. Все их имена начинаются с префикса '':

 

Заполнение памяти:

 

Функция копирует значение val (интерпретируемое как) в первые байтов буфера. Она особенно полезна для обнуления блоков динамической памяти:

 

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

 

Копирование памяти:, и

 

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

 

Это простейшая функция. Она копирует байтов из в. Она не обрабатывает перекрывающиеся области памяти. Функция возвращает.

 

Подобно, она также копирует байтов из в. Однако, она обрабатывает перекрывающиеся области памяти. Функция возвращает.

 

Эта копирует байты из в, останавливаясь либо после копирования в, либо после копирования байтов. Если она находит, то возвращает указатель на положение в сразу за. В противном случае возвращается.

Теперь, в чем проблема с перекрывающейся памятью? Рассмотрим рис. 12.1.

Рис. 12.1. Перекрывающиеся копии

Целью является скопировать четыре экземпляра от до в участок от до. Здесь проблемой является, побайтовое копирование с перемещением в памяти из затрет до того, как он будет безопасно скопирован в! (Может возникнуть также сценарий, когда копирование в памяти в обратном направлении разрушит перекрывающиеся данные.)

Функция была первоначальной функцией в System V API для копирования блоков памяти; ее поведение для перекрывающихся блоков памяти не была подробно определена тем или иным способом. Для стандарта С 1989 г. комитет почувствовал, что это отсутствие определенности является проблемой, поэтому они придумали. Для обратной совместимости была оставлена, причем поведение для перекрывающейся памяти было специально отмечено как неопределенное, а в качестве процедуры, корректно разрешающей проблемные случаи, была предложена.

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

Как для, так и для (как и для) буфер назначения является первым аргументом, а источник – вторым. Чтобы запомнить это, обратите внимание на порядок, который тот же самый, как в операторе присваивания:

 

(Справочные страницы во многих системах не помогают, предлагая прототип в виде '' и полагаясь на то, что текст объяснит, что есть что. К счастью, справочная страница GNU/Linux использует более осмысленные имена.)

 

Сравнение блоков памяти:

 

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

Вы можете поинтересоваться: «Почему бы не использовать для такого сравнения?» Разница между двумя функциями в том, что не принимает во внимание нулевые байты (завершающий строку ''.) Таким образом, является функцией, которая используется, когда вам нужно сравнить произвольные двоичные данные.

Другим преимуществом является то, что она быстрее типичной реализации на C:

 

 

Скорость может быть достигнута в результате использования специальных инструкций «блочного сравнения памяти», которые поддерживают многие архитектуры, или в результате сравнения единиц, превышающих байты. (Эта последняя операция коварна, и лучше оставить ее автору библиотеки.)

По этим причинам всегда следует использовать вашу библиотечную версию вместо прокручивания своей собственной. Велика вероятность, что автор библиотеки знает машину лучше вас

 


Поделиться с друзьями:

Папиллярные узоры пальцев рук - маркер спортивных способностей: дерматоглифические признаки формируются на 3-5 месяце беременности, не изменяются в течение жизни...

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...

История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.011 с.