Чтение онлайн

на главную - закладки

Жанры

Язык Си - руководство для начинающих

Д. МАРТИН

Шрифт:

4. Создайте программу, которая удваивает пробелы в тексте с одиночными пробелами.

11. Препроцессор языка Си

ДИРЕКТИВЫ ПРЕПРОЦЕССОРА СИМВОЛЬНЫЕ КОНСТАНТЫ МАКРООПРЕДЕЛЕНИЯ И "МАКРОФУНКЦИИ" ПОБОЧНЫЕ ЭФФЕКТЫ МАКРООПРЕДЕЛЕНИИ ВКЛЮЧЕНИЕ ФАЙЛОВ УСЛОВНАЯ КОМПИЛЯЦИЯ

ДИРЕКТИВЫ ПРЕПРОЦЕССОРА #define, #include, #undef, #if, #ifdef, #ifndef, #else, #endif

Язык Си был разработан в помощь работающим программистам, а им нравится его препроцессор. Этот полезный помощник просматривает программу до компилятора (отсюда и термин "препроцессор")

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

СИМВОЛИЧЕСКИЕ КОНСТАНТЫ: #define

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

/* простые примеры директивы препроцессора */

#define TWO 2 /* по желанию можно использовать комментарии */

#define MSG "Старый серый кот поет веселую песню."

/* обратная косая черта продолжает определение на следующую строку */

#define FOUR TWO *TWO

#define PX printf("X равен %d.\n", x)

#define FMT "X равен %d.\n"

main

{

int x = TWO;

PX;

x = FOUR;

printf(FMT, x);

printf( "%s\n", MSG);

printf("FTWO: MSG\n");

}

Каждая строка состоит из трех частей. Первой стоит директива #define. Далее идет выбранная нами аббревиатура, известная у программистов как "макроопределение". Макроопределение не должно содержать внутри себя пробелы. И наконец, идет строка (называемая "строкой замещения"), которую представляет макроопределение. Когда препроцессор находит в программе одно из ваших макроопределений, он почти всегда заменяет его строкой замещения. (Есть одно исключение, которое мы вам сейчас покажем.) Этот процесс прохождения от макроопределения до заключительной строки замещения называется "макрорасширением". Заметим, что при стандартной форме записи на языке Си можно вставлять комментарии; они будут игнорироваться препроцессором. Кроме того, большинство систем разрешает использовать обратную косую черту ('\') для расширения определения более чем на одну строку.

"Запустим" наш пример, и посмотрим за его выполнением.

Х равен 2.

Х равен 4.

Старый серый кот поет веселую песню. TWO: MSG

Вот что произошло. Оператор int x = TWO;

превращается в int x = 2;.

 

РИС. 11.1. Части макроопределения.

т. е. слово TWO заменилось цифрой 2. Затем оператор РХ; превращается в printf("X равно %d. \n", х); поскольку сделана полная замена. Это новшество, так как до сих пор мы использовали макроопределения только для представления констант. Теперь же мы видим, что макроопределение может представлять любую строку, даже целое выражение на языке Си. Заметим, однако, что это константная строка; РХ напечатает только переменную, названную x.

Следующая строка также представляет что-то новое. Вы можете подумать, что FOUR заменяется на 4, но на самом деле выполняется следующее:

х = FOUR;

превращается в х = TWO *TWO; превращается в х = 2*2; и на этом все заканчивается. Фактическое умножение имеет место не во время работы препроцессора и не при компиляции, а всегда без исключения при работе программы. Препроцессор не выполняет вычислений; он только очень точно делает предложенные подстановки.

Заметим, что макроопределение может включать другие определения. (Некоторые компиляторы не поддерживают это свойство "вложения".)

В следующей строке printf(FMT, х); превращается в printf(" Х равно %d.\n", х) когда FMT заменяется соответствующей строкой. Этот подход может оказаться очень удобным, если у вас есть длинная строка, которую вы используете несколько раз.

В следующей строке программы MSG заменяется соответствующей строкой. Кавычки делают замещающую строку константой символьной строки; поскольку программа получает ее содержимое, эта строка будет запоминаться в массиве, заканчивающемся нуль-символом. Так, #define HAL 'Z' определяет символьную константу, а #define НАР "Z" определяет символьную строку: Z\0.

Обычно препроцессор, встречая одно из ваших макроопределений в программе, очень точно заменяет их эквивалентной строкой замещения. Если эта строка также содержит макроопределения, они тоже замешаются. Единственным исключением при замене является макроопределение, находящееся внутри двойных кавычек. Поэтому printf("TWO: MSG"); печатает буквально TWO: MSG вместо печати следующей строки:

2: "Старый серый кот поет веселую песню."

Если вам нужно напечатать эту строку, можно использовать оператор

printf("%d: %s\n", TWO, MSG);

потому что здесь макроопределения находятся вне кавычек.

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

Поделиться:
Популярные книги

Запасная дочь

Зика Натаэль
Фантастика:
фэнтези
6.40
рейтинг книги
Запасная дочь

Гридень 2. Поиск пути

Гуров Валерий Александрович
2. Гридень
Детективы:
исторические детективы
5.00
рейтинг книги
Гридень 2. Поиск пути

Землянка для двух нагов

Софи Ирен
Фантастика:
космическая фантастика
5.00
рейтинг книги
Землянка для двух нагов

Отверженный VI: Эльфийский Петербург

Опсокополос Алексис
6. Отверженный
Фантастика:
городское фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Отверженный VI: Эльфийский Петербург

Проданная невеста

Wolf Lita
Любовные романы:
любовно-фантастические романы
5.80
рейтинг книги
Проданная невеста

Флеш Рояль

Тоцка Тала
Детективы:
триллеры
7.11
рейтинг книги
Флеш Рояль

Шведский стол

Ланцов Михаил Алексеевич
3. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Шведский стол

Найденыш

Шмаков Алексей Семенович
2. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
5.00
рейтинг книги
Найденыш

Семья. Измена. Развод

Высоцкая Мария Николаевна
2. Измены
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Семья. Измена. Развод

Конструктор

Семин Никита
1. Переломный век
Фантастика:
попаданцы
альтернативная история
4.50
рейтинг книги
Конструктор

Снегурка для опера Морозова

Бигси Анна
4. Опасная работа
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Снегурка для опера Морозова

Русь. Строительство империи

Гросов Виктор
1. Вежа. Русь
Фантастика:
альтернативная история
рпг
5.00
рейтинг книги
Русь. Строительство империи

Идеальный мир для Лекаря 14

Сапфир Олег
14. Лекарь
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 14

Кротовский, не начинайте

Парсиев Дмитрий
2. РОС: Изнанка Империи
Фантастика:
городское фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Кротовский, не начинайте