Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Средство _Generic можно применять для определения макросов, которые действуют подобно функциям, не зависящим от типа (“обобщенным”). В разделе, посвященном библиотеке math, далее в главе будет приведен пример.
Встраиваемые функции (С99)
Обычно с вызовом функции связаны накладные расходы. Это означает, что подготовка вызова, передача аргументов, переход к коду функции и возврат требуют време ни на выполнение. Как вы уже видели, макрос можно использовать для встраивания кода, тем самым избегая таких накладных расходов. В стандарте С99 был позаимствован у C++ (но не во всем точно) другой подход — встраиваемые функции. Исходя из его названия, вы могли бы ожидать, что встраиваемая функция заменяет вызов функции встраиваемым кодом, но это не так. В стандартах С99 и C11 на самом деле указано так: “превращение функции во встраиваемую предполагает, что ее вызов будет настолько быстрым, насколько это возможно. Степень, до которой подобные предположения эффективны, зависит от реализации”. Таким образом, преобразование функции во встроенную может привести к тому, что компилятор заменит вызов функции встраиваемым кодом и/или предпримет оптимизации другого рода либо вообще не окажет никакого воздействия.
Существуют разные способы создания определений встраиваемых функций. В стандарте говорится о том, что функция с внутренним связыванием может быть сделана встраиваемой, и данное определение для встраиваемой функции должно находиться в том же файле, где функция применяется. Поэтому простой подход предполагает использование спецификатора функции inline наряду со спецификатором класса хранения static. Как правило, встраиваемые функции определяются до их первого применения в файле, так что определение действует также и в качестве прототипа. Другими словами, код будет выглядеть примерно так:
Встретив встраиваемое объявление, компилятор может, к примеру, заменить вызов функции eatline() ее телом. Это значит, что результат может быть такой, как если бы вы взамен написали следующий код:
#include
inline static void eatline() // встраиваемое определение/прототип
{
while (getchar() != '\n') continue;
}
Препроцессор и библиотека С 689
Поскольку встраиваемая функция не имеет отдельного предназначенного для нее блока кода, получить ее адрес нельзя. (В действительности это возможно, но тогда компилятор сгенерирует функцию, отличную от встраиваемой.) Кроме того, встраиваемая функция может быть не видна в отладчике.
Встраиваемая функция должна быть короткой. Для длинной функции время, затрачиваемое на ее вызов, невелико по сравнению со временем выполнения тела функции, поэтому использование встраиваемой функции не обеспечит существенной экономии времени.
Для проведения оптимизаций по встраиванию функции компилятору должно быть известно содержимое определения функции. Это означает, что определение встраиваемой функции должно находиться в том же файле, что и ее вызов. По данной причине встраиваемая функция обычно имеет внутреннее связывание. Следовательно, если программа состоит из нескольких файлов, встраиваемое определение понадобится поместить в каждый файл, который вызывает функцию. Для достижения такого условия проще всего указать определение встраиваемой функции в заголовочном файле и затем включать этот файл в файлы, где функция применяется.
Встраиваемая функция является исключением из правила, которое не рекомендует помещать исполняемый код в заголовочный файл. Так как встраиваемая функция имеет внутреннее связывание, ее определение в нескольких файлах не вызывает проблем.
В отличие от C++, язык С разрешает также смешивать встраиваемые определения с внешними определениями (определениями функций с внешним связыванием). Например, рассмотрим программу, состоящую из следующих трех файлов:
690 глава 16
Первый файл содержит определение inline static, как и ранее. Второй файл имеет определение обычной функции, отсюда и наличие внешнего связывания. Третий файл включает определение inline, в котором не указан квалификатор
static.
Что здесь происходит? Функция spam ( ) в file2.c использует определение square() из этого файла. Данное определение, имея внешнее связывание, является видимым другим файлам, но main() в filel. с применяет локальное определение static функции square(). Поскольку это определение также inline, компилятор может (или нет) оптимизировать код, возможно, встроив его. Наконец, для file3.c компилятор свободен в использовании либо встраиваемого определения из file3.c, либо определения с внешним связыванием из file2 .с (или обоих!). Если вы не укажете static в определении inline, как в файле file3.c, то определение inline рассматривается в качестве альтернативы, которая могла бы применяться вместо внешнего определения.
Обратите внимание, что до появления С99 встраиваемые функции в GCC были реализованы с использованием несколько отличающихся правил, так что интерпретация GCC спецификатора inline может зависеть от указанных флагов компилятора.
ФУНКЦИИ _Noreturn (С11)
Когда в стандарте С99 появилось ключевое слово inline, оно было единственным примером спецификатора функции. (Ключевые слова extern и static называются спецификаторами класса хранения и могут применяться к объектам данных, а также к функциям.) В стандарт С11 был добавлен второй спецификатор функции, _Noreturn, предназначенный для указания функции, которая по завершении не возвращает управление вызывающей функции. Примером функции _Noreturn является exit(); после обращения к ней вызывающая функция никогда не возобновит свое выполнение. Обратите внимание, что это отличается от возвращаемого типа void. Типичная функция void возвращает управление вызывающей функции; она просто не предоставляет какое-либо значение.
Цель Noreturn заключается в том, чтобы проинформировать пользователя и компилятор, что конкретная функция не возвратит управление вызывающей программе. Информирование пользователя помогает предотвратить неправильное употребление функции, а указание на такой факт компилятору может сделать возможными некоторые оптимизации кода.
Библиотека С
Читать дальшеИнтервал:
Закладка: