Интернет магазин китайских планшетных компьютеров



Компьютеры - Препроцессор Си - Функции

23 января 2011


Оглавление:
1. Препроцессор Си
2. Директивы
3. Функции



Включение

Препроцессор Си, встречая следующие директивы:

#include "..."

или

#include <...>

полностью копирует содержимое указанного файла в файл, в котором указана эта директива, в месте вызова директивы. Эти файлы обычно содержат определение интерфейса для различных функций библиотек и типов данных, которые должны быть подключены перед их использованием; таким образом, директива #include обычно указывается в начале файла. По этой причине подключаемые файлы и называются заголовочными. Некоторые содержат примеры из стандартной библиотеки Си, обеспечивая математические функции и функции ввода-вывода соответственно.

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

Начиная с 1970-х среди программистов все большее распространение и известность получают более быстрые, безопасные и эффективные альтернативные способы переиспользования подключения файлов, применяемых в большинстве языков программирования: Java и Common Lisp используют пакеты, Паскаль использует юниты, Modula, OCaml, Haskell и Python используют модули, а D, разработанный как замена языков Си и C++, использует импорт.

Макросы

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

Например,

#define max > ? :)

определяет макрос max, использующий два аргумента a и b. Этот макрос можно вызывать как любую Си-функцию, используя схожий синтаксис. То есть, после обработки препроцессором,

z = max;

становится

z =  >  ?  : );

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

Например, если f и g — две функции, вызов

z = max, g);

не вычислит один раз fи один раз g, и поместит наибольшее значение в z, как этого можно было ожидать. Вместо этого одна из функций будет вычислена дважды. Если функция имеет побочные эффекты, то вероятно, что её поведение будет отличаться от ожидаемого.

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

Некоторые современные языки обычно не используют такой способ метапрограммирования с использованием макросов как дополнений строк символов, в расчете или на автоматическое или на ручное подключение функций и методов, а вместо этого другие способы абстракции, такие как шаблоны, общие функции или параметрический полиморфизм. В частности, встраиваемые функции позволяют избежать одного из главных недостатков макросов в современных версия Си и C++, так как встроенная функция обеспечивает преимущество макросов в снижении накладных расходов при вызове функции, но её адрес можно передавать в указателе для косвенных вызовов или использовать в качестве параметра. Аналогично, проблема множественных вычислений, упомянутая выше в макросе max, для встроенных функций неактуальна.

Условная компиляция

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

В общем случае, программисту необходимо использовать конструкцию наподобие этой:

#ifndef FOO_H
#define FOO_H
......
#endif

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

Условия препроцессора можно задавать несколькими способами, например:

#ifdef x
...
#else
...
#endif

или

#if x
...
#else
...
#endif

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

Большинство современных языков программирования не используют такие возможности, больше полагаясь на традиционные операторы условия if...then...else..., оставляя компилятору задачу извлечения бесполезного кода из компилируемой программы.



Просмотров: 2888


<<< Система управления базами данных