ДИРЕКТИВЫ ПРЕПРОЦЕССОРА
Препроцессор - это программа, которая производит некоторые, иногда весьма значительные, манипуляции с первоначальным текстом программы перед тем, как он подвергается трансляции.
Оператор препроцессора - это одна строка исходного текста, начинающаяся с символа #, за которым следуют название оператора и операнды.
Операторы препроцессора могут появляться в любом месте программыи их действие распространяется на весь исходный файл.
Весьма часто используют следующие операторыпрепроцессора:
#include
#define
Более специфичными являются директивы #pragma, #if, #error и др.
Важная возможность препроцессора - включение в исходный текст содержимого других файлов. Эта возможность, в основном, используется для того, чтобы снабжать программы какими-то общими для всех данными, определениями.
Например, чрезвычайно часто в начале программы на языке Си встречается препроцессорная конструкция
#include <stdio.h>
Когда исходный текст программы обрабатывается препроцессором, на место этой инструкции ставится содержимое расположенного в некоем стандартном месте файла stdio.h, содержащего макроопределения и объявления данных, необходимых для работы функций из стандартной библиотеки ввода-вывода. Для использования различных математических функций необходимо подключать файл описаний math.h. Функции, оперирующие со строками, описаны в файле string.h, функции общего назначения - в stdlib.h, функции даты и времени - в time.h, диагностика - в assert, h и т.д.
Директива #define позволяет дать в программе макроопределения (или задать макросы). Оператор макроопределения имеет вид
#define <макроимя> <строка лексем> или
#define <макроимя(<список параметров>)> <строка лексем>
Макроимя - это идентификатор. Строка лексем - это последовательность лексем от Макроимени до конца строки. Точка с запятой в конце макроопределения не ставится.
Препроцессорная обработка макроопределения сводится к тому, что любое появление Макроимени (макровызов) в качестве отдельной лексемы в тексте программы, расположенном после макроопределения, ведет к замене этого Макроимени на указанную Строку лексем.
Например, прочитав определения
#defmePI3.14159
#defineE2.711828
препроцессор заменит в программе все имена PI и Е на соответствующие числовые константы.
Препроцессор языка Си позволяет переопределять не только константы, но и целые программные конструкции. Например, можно написать определение
#define forever for(;;)
и затем всюду писать бесконечные циклы в виде forever
Определение макроимени с параметрами имеет некоторую специфику. Список параметров макроимени - это список идентификаторов, разделенных запятыми. Следующая после списка параметров строка лексем также может содержать эти параметры, которые при макровызове будут заменены на соответствующие аргументы.
Макровызов должен быть отдельной лексемой и состоять из макроимени и заключенного в круглые скобки списка аргументов. При обработке макровызова препроцессор заменяет каждый параметр в строке лексем на соответствующий аргумент макровызова.
В следующих программах иллюстрируются некоторые применения операторов препроцессора.
Пример 1: найти большее из двух чисел.
Программа 110
#include<stdio.h>
#define MAX(X,Y) ((X)>(Y) ? (X) : (Y))
main()
{
int x,y;
scanf ("%d %d", &x, &y); printf ("%d", MAX(x, y) );
)
Результат работы программы:
3 5
Пример 2.
Программа 111
#include<stdio.h>
#define S (x) x*x
#define P(x) printf("x равен %d.\n",x)
main()
{
int x=4;
int z ;
z = S(x); P(z); z = 3(2);
P(z);
P(S(x));
P(S(x+2));
P(100/S(2));
P(S(++x)) ;
}
Результат работы программы:
x равен16.
x равен 4.
x равен 16.
x равен 14.
x равен 100.
x равен 30.
Оператор препроцессора #pragma позволяет записывать самые различные указания компилятору (зависящие от конкретного компилятора). Например, следующие два предложения препроцессора
#pragma recursive
#pragma nonrec
устанавливают режим всех функций программы по умолчанию рекурсивным или нерекурсивным. Указание препроцессора
#pragma optimize time
воспринимается компилятором таким образом, что он старается сгенерировать объектный код, отличающийся более высокой скоростью выполнения, чем в случае, когда он должен быть более компактным.
СИ И ПАСКАЛЬ
При знакомстве с языком Си, особенно после изучения Паскаля и Бейсика, погружение в детали его изобразительных средств может затушевать важную мысль: хотя на Си можно написать практически любую прикладную программу, он изначально для этого не предназначен. Си является результатом эволюционного развития языков создания системных программных средств. Если в прикладном программировании эволюция шла от Фортрана к Алголу, Коболу, Паскалю и т.д., то в системном - от Ассемблеров, привязанных к архитектуре ЭВМ, к Си, для которого созданы трансляторы, делающие его хоть и независимым от архитектуры, но не меняющим основного предназначения.
С помощью Си можно сделать то, что на Паскале сделать невозможно (или почти невозможно) - например, написать фрагмент операционной системы (или новую операционную систему), утилиты и т.п. Так, ряд трансляторов с Паскаля написаны на Си; обратное невозможно представить. В то же время, не раз отмечалось, что прикладные программы, написанные на Паскале, отличаются большей надежностью, чем написанные на Си; их легче читать, передавать от одного программиста другому для совершенствования и сопровождения. Это связано с тем, что Паскаль содержит существенно больше ограничений и является языком более высокого уровня с сильной типизацией данных. Для языка же. который предназначен для разработки системного программного обеспечения, чем меньше ограничений, тем лучше; так, в Си возможны неявные преобразования всех базовых типов данных и указателей друг в друга, что крайне желательно при создании системных средств, но при невнимательности программиста приводит к ошибкам, не улавливаемым транслятором с Си (Паскаль же подобные недопустимые операции пресекает немедленно).
Разумеется, сказанное выше не следует абсолютизировать. Программисты, привыкшие к Си, успешно пишут на нем программы различных классов. Это касается не только Си - вспомните об экспертных системах, написанных на Бейсике. В то же время, при массовом программировании придерживаться «разделения труда» между языками представляется более естественным.
Контрольные вопросы и задания
1. Охарактеризуйте назначение и особенности языка Си.
2. Какие символы образуют алфавит языка Си?
3. Что называется лексемами, идентификаторами, литералами? Приведите примеры.
4. Какие типы данных используются в Си? Приведите примеры описания переменных.
5. Охарактеризуйте арифметические, логические и битовые операции Си.
6. Какие разновидности оператора присваивания имеются в Си?
7. Как на языке Си можно описать ветвление?
8. Охарактеризуйте возможности цикла for. Приведите примеры.
9. Какие логические циклы имеются в Си? Приведите примеры их использования.
10. Какие операторы управления имеются в Си?
11. Какова структура программы на Си? Что такое функция?
12. Приведите примеры использования функций (с аргументами и без, возвращающих и не возвращающих значения).
13. Для чего в качестве аргументов функций используются указатели? Приведите примеры.
14. Для чего в Си существуют классы памяти?
15. Что такое потоки и файлы в Си?
16. Охарактеризуйте стандартные функции ввода и вывода в Си.
17. Что такое препроцессор Си? Приведите примеры директив препроцессора.
Дата добавления: 2020-02-05; просмотров: 537;