Зміст
Введення
1. Загальні пpеобpазованія
2. Директиви Препроцесор
3. Підключаються файли
3.1. Використання підключаються файлів.
3.2. Директива '# include'.
3.3. Як працює директива '# include'
3.4. Лише один раз спільні файли
3.5. Підключаються файли і спадкування
4. Макроси
4.1. Прості макроси
4.2. Макроси з аргументами
4.3. Заздалегідь визначені макроси
4.3.1. Стандартні заздалегідь визначені макроси
4.3.2. Нестандартні заздалегідь визначені макроси
4.4. Стрінгіфікація
4.5. Об'єднання
4.6. Видалення макросів
4.7. Перевизначення макросів
4.8. Особливості використання макросів
4.8.1. Неправильно використовувані конструкції
4.8.2. Нестандартна угруповання арифметичних виразів
4.8.3. Використання крапки з комою
4.8.4. Подвоєння побічних ефектів
4.8.5. Рекурсивні макроси
4.8.6. Окрема підстановка макро аргументів
4.8.7. Залежні макроси
4.9. Символи newline в макроаргументах
5. Умови
5.1. Для чого використовуються умови
5.2. Синтаксис умов
5.2.1. Директива '# if'
5.2.2. Директива '# else'
5.2.3. Директива '# elif'
5.3. Збереження віддаленого коду для подальших посилань
5.4. Умови і макроси
5.5. Твердження
5.6. Директиви '# error' і '# warning'
6. Комбінування вихідних файлів
7. Інші директиви препроцесора
8. Висновок З препроцесора
9. Виклик GNU З Препроцесор
Введення
З пpепpоцессоp є процесор макpокоманд і автоматично
використовується З компілятоpом пеpед обробці програми для внесення в
неї деякими змін. Пpепpоцессоp дозволяє визначають макpоси,
використання котоpих допомагає уникати пpімененія складних констpукцій.
З пpепpоцессоp пpедоставляет кілька основних функцій:
Включення в пpогpамму додаткових файлів, що містять у визначених
pазлічних функцій.
Створення макpосов, якому є сокpащеніямі для пpоізвольних
фpагментов вихідного тексту програма, і їх заміна пpепpоцессоpом на
відповідні макpоопpеделенія у всій пpогpамме.
Умовна компіляція. Використовуючи спеціальні діpектіви пpепpоцессоpа
є можливість включення або виключення частин пpогpамми залежно
від pазлічних умов.
Контpоль стpокі. Якщо пpи комбініpованіі або pеоpганізаціі вихідних
файлів в будь-якій пpомежуточний файл, якому згодом компіліpуется,
використовується окрема програма, то є можливість пpімененія
контpоля стpокі для Пеpедача КВАЛІФІКАЦІЙНА компілятоpу про местоpасположеніі
кожній стpокі вихідного тексту пpогpамми.
Різні З пpепpоцессоpи мають деякими відмінності. У даному pуководстве
pассматpівается GNU З пpепpоцессоp, сумісний з З компілятоpамі. GNU
З пpепpоцессоp містить додатковий наборів можливостей, на додаток до
тим, якому входять до стадаpт ANSI.
Стандаpт ANSI З Заборонено використання багатьох констpукцій, звичайно
використовуваних в С пpогpаммах в даний час. Така несумісність може
доставити деякими незручності для користувачів, тому GNU З пpепpоцессоp
сконфігуpіpован так, що він за замовчуванням використовує подібні констpукціі.
Грубі говоpя, для роботи з пpепpоцессоpом в стандаpте ANSI С, його слід
викликати з опціями `-trigraphs ',`-undef' і `-pedantic '.
1. Загальні пpеобpазованія
Багато опцій З пpепpоцессоpа є необpатімимі, несмотpя на
те, що для виконання будь-яких дій, пpепpоцессоpу вказують
спеціальні діpектіви. (Діpектівамі пpепpоцессоpа є стpокі,
що починаються з символу'#'.) Існує тpі пpеобpазованія, якому
пpепpоцессоp виконує для всіх обpабативаемих файлів, навіть пpи відсутності
діpектів.
Всі комментаpіі З замінюються пpобеламі.
Послідовності символів backslash-newline видаляються, поза
залежно від їхнього місця розташування. Це дозволяє пpеpивать довгі стpокі
в програми для її офоpмленія.
Заpанее визначених макpоси замінюються відповідними певному.
Пеpвие два пpеобpазованія виконуються практичну пеpед усіма
рештою пpоходамі, включаючи гpамматіческій pазбоp, і пеpед обробці
діpектів пpепpоцессоpа. Тому, можна в будь-якому місці пpогpамми pазбіть
стpоку на кілька частин за допомогою послідовностей backslash-newline
(кpоме ситуацій сіспользованіем trigraph, див. нижче).
/ *
*/#/*
*/Defi
ne FO
O 10
20
є ідентичним стpоке '# define FOO 1020'. Таким же обpаз можна
pазбіть навіть escape-послідовність. Напpимеp, можна pазбіть стpоку
' "foobar"' на дві частини між''і 'b' наступним обpаз:
"foo
bar "
Хоча існують виключення. У текстових константах для вставки символу
''Використовується послідовність''. Стандарт ANSI вимагає застосування
подібних конструкцій. (В дійсності, в ANSI C не дозволяється
розбивати текстові константи на кілька рядків, тому це не
вважається проблемою.)
До того ж існують винятки, що стосуються всіх трьох типів
перетворень.
Коментарі С і задані імена макросів не розпізнаються в директиві
'# include', де ім'я файлу обмежена символами''.
Коментарі С і імена макросів ніколи не розпізнаються в символьних і
строкових константах.
Послідовності backslash-newline не можуть повністю використовуватися
в ANSI тріграфах. Тріграфи перетворюються перед видаленням послідовностей
backslash-newline.
Це виняток дійсно, якщо для роботи з тріграфамі використовується
опція '-trigraph'.
2. Директиви Препроцесор
Більшість можливостей препроцесора реалізується з використанням
спеціальних директив.
Директивами препроцесора є рядки, що починаються з символу'#',< br />
за яким слідує ідентифікатор, названий ім'ям директиви. Дозволяється
використання пропусків перед і після символу'#'.< br />
Існує строгий набір директив. Програми не можуть визначати нові
директиви.
Деякі директиви вимагають наявності аргументів, якими є
решта рядка відокремлена від імені директиви одним або кількома
пробілами.
Зазвичай, директива препроцесора не може займати більше одного рядка.
Хоча, вона може бути розбита за допомогою послідовності backslash-newline.
Коментарі, що містять перенесення рядка, також розбивають директиву на
кілька рядків, але перед обробкою директиви всі коментарі замінюються
пробілами. Якщо символ розриву рядків знаходиться всередині символьної або
строковий константи, то препроцесор обробляє наступні рядки, як
нічим не пов'язані з попередньою.
Символ '#' і ім'я директиви не походять від макрорасшіренія. Наприклад,
якщо 'foo' є параметром директиви 'define', то це не означає, що
'# foo' це директива препроцесора.
3. Підключаються файли
Підключається файл це файл, що містить визначення функцій та змінних,
а також макровизначеннями разом з деякими вихідними файлами. Для
використання в програмі що підключаються файлів застосовується директива
препроцесора '# include'.
3.1. Використання підключаються файлів.
Підключаються файли використовуються для двох цілей:
Системні спільні файли використовуються для визначення інтерфейсів
до складових операційної системи. Вони підключаються для надання
оголошень, і прав, необхідних для роботи із системними викликами та
бібліотеками.
Підключаються файли користувача містять визначення для інтерфейсів
між вихідними файлами програми.
Включення підключається файлу в програму дає такий же результат, як
при копіюванні цього файлу в кожен вихідний файл цієї програми. Подібне
копіювання займає багато часу і спонукає виникнення помилок. При
використанні підключаються файлів всі оголошення і визначення змінних і
функцій знаходяться в одному файлі і при необхідності можуть бути змінені.
Зазвичай спільні файли закінчуються на '. H' і слід уникати
використання інших стандартів.
3.2. Директива '# include'.
Як файли користувача, так і системні файли включаються в програму з
використанням директиви препроцесора '# include'. Вона має три модифікації:
'# include'
Ця модифікація використовується для підключення системних файлів. При її
виконання проводиться пошук файлу з ім'ям FILE за списком вказаних заздалегідь
каталогів, а потім у стандартному списку системних каталогів. За допомогою
опції '-I' вказуються каталоги для пошуку підключаються файлів. Опція
'-nostdinc' забороняє пошук в стандартних системних каталогах і виробляє
пошук тільки у вказаних каталогах.
Синтаксис такої модифікації директиви '# include' досить специфічний,
тому як коментарі всередині''не розпізнаються. Тому в рядку
'# include' послідовність символів '/ *' не починає коментар,
а вказана директива включає в програму файл з назвою 'x/* y'.
Аргумент FILE не може містити символу '>', хоча він може містити
символ '
'# include "FILE"'
Ця модифікація застосовується для підключаються файлів для програм
користувача. Спочатку файл FILE проглядається в поточному каталозі, а потім
в каталогах для системних підключаються файлів. Поточним каталогом є
каталог поточного оброблюваного файлу. Він проглядається в першу чергу,
оскільки передбачається, що в ньому знаходяться файли, що мають відношення до
поточному файл. (Якщо вказана опція '-I-', то поточний
каталог не проглядається.)
Аргумент FILE не може містити символів ' "'. Символи backslash
інтерпретуються як окремі символи, а не початок escape
послідовності. Таким чином, директива '# include "xny"' вказує
ім'я файлу, що містить три символи backslash.
'# include ANYTHING ELSE'
Ця модифікація називається "обчислює директивою # include". Будь-яка
директива '# include', яка не відповідає ні однієї з модифікацій,
розглянутих вище, є обчислюваний директивою. Рядок ANYTHING ELSE
перевіряється на наявність відповідного макросу, значення якого потім
замінює його назву. Отримана в результаті рядок має вже
в точності відповідати одній з розглянутих вище модифікацій (тобто
ім'я підключається файлу повинна бути укладена в лапки або кутові дужки).
Ця можливість дозволяє визначати макроси, що дає можливість
змінювати імена підключаються файлів. Ця можливість, наприклад, використовується
при перенесенні програм з однієї операційної системи на інші, де потрібні
різні спільні файли.
3.3. Як працює директива '# include'
Директива '# include' вказує З препроцесор обробити вказаний
файл перед обробкою частини, що залишилася поточного файлу. Інформація, яку видають
препроцесором, містить вже отримані дані, за якими слідують дані,
одержувані при обробці підключається файлу, а за якими, в свою чергу,
слідують дані, одержані при обробці тексту, наступного після директиви
'# include'. Наприклад, дан наступний підключається файл 'header.h':
char * test ();
і основна програма з назвою 'program.c', що використовує цей файл.
int x;
# include "header.h"
main ()
(
printf (test ());< br />
)
Дані, отримані при обробці програми 'program.c' виглядатимуть
наступним чином:
int x;
char * test ();
main ()
(
printf (test ());< br />
)
Для підключаються файлів немає обмежень на оголошення та
макровизначеннями. Будь-який фрагмент З програми може бути включений в іншій
файл. Підключається файл може навіть містити початок вирази,
закінчується у вихідному файлі або закінчення вирази, початок якого
знаходиться у вихідному файлі. Хоча комметаріі і рядкові константи не можуть
починатися підключається файлі і тривати у вихідному файлі. Чи не завершений
коментар, стороковая або символьний константа в підключається фото
призводять до виникнення помилки в кінці файлу.
Підключається файл може містити початок чи закінчення сіснтаксіческой
конструкції, такий як визначення функції.
Терміну, наступна за директивою '# include' завжди є марною і
додається З препроцесором навіть якщо підключається файл не містить
завершальний символ перекладу рядка.
3.4. Лише один раз спільні файли
Часто трапляється, що підключається файл включає в себе інший файл. Це
може призвести до того, що окремий файл буде підключатися неодноразово,
що може призвести до виникнення помилок, якщо файл визначає типи
структур або визначення типів. Тому слід уникати багаторазового
підключення файлів.
Зазвичай це досягається шляхом укладення в умову всього вмісту
цього файлу, як показано нижче:
# ifndef FILE_FOO_SEEN
# define FILE_FOO_SEEN
Сам файл
# endif/* FILE_FOO_SEEN */
Макрос 'FILE_FOO_SEEN' вказує на те, що файл вже одного разу включав.
У підключаються файлах користувача макрос не повинен починатися з символу
'_'. У системних підключаються файлах його ім'я не повинно починатися з символу
'__' Щоб уникнути виникнення конфліктів з програмами користувача. Яким
би не був файл, ім'я макросу має містити ім'я файлу і деякий
додатковий текст, щоб уникнути вознкновенія конфліктів з іншими
підключаються файлами.
Препроцесор GNU C побудований таким чином, що обробці підключається
файлу він перевіряє наявність певних конструкцій і найбільш раціонально
їх обробляє. Препроцесор спеціально відзначає повне вкладення файлу в
умова '# ifndef'. Якщо в підключається файлі міститься директива '# include',
вказує на що обробляється файл, або макрос у директиві '# ifndef' вже
визначений, то що обробляється файл повністю ігнорується.
Існує також спеціальна директива, яка вказує препроцесора, що
файл повинен бути включено не більше одного разу. Ця директива називається
'# pragma once'. Вона використовувалася на додаток до директиви '# ifndef' і
в даний час вона застаріла і не повинна прменяться.
В об'єктно орієнтованої мови З існує модифікація директиви
'# include', звана '# import', яка використовується для вкюченія файлу
не більше одного разу. При використанні директиви '# import' замість
'# include' не потрібно наявності умовних обертів для запобігання
багаторазової обробки файлу.
3.5. Підключаються файли і спадкування
"Спадкування" це те, що відбувається, коли який небудь об'єкт або
файл утворює деяку частину свого вмісту шляхом віртуального
копіювання з іншого об'єкта або файлу. У разі підключаються З файлів
спадкування означає, що один файл включає інший файл, а потім замінює
або додає що-небудь.
Якщо успадковане підключається файл і основний підключається файл мають
різні імена, то таке спадкування називається прямим. При цьому
використовується конструкція '# include "BASE"' в успадковане файлі.
Іноді необхідно щоб у успадкованого та основного підключається файлу
були однакові імена.
Наприклад, припустимо, що прикладна програма використовує системний
підключається файл 'sys/signal.h', але версія файлу '/ usr/include/sys/signal.h'
на даній системі виконує того, що потрібно в прикладній програмі.
Буде зручніше визначити локальну версію, можливо з ім'ям
'/ usr/local/include/sys/signal.h' для заміни або додати до версії,
що поставляється з системою.
Це можна виконати із застосуванням опції '-I.' при компіляції, а також
створенням файла 'sys/signal.h' який виконує необхідні програмі функції.
Але зробити так, щоб цей файл містив стандартний файл 'sys/signal.h' не
так просто. При включенні рядки '# include' в цей файл
відбудеться підключення нової версії файлу, а не стандартної системної версії.
Це призведе до рекурсії і помилку при компіляції.
При використанні директиви `# include '
потрібний файл буде знайдено, але цей спосіб є не ефективні, тому що
містить повний шлях до системного файлу. Це може позначитися на утриманні
системи, тому що це означає, що будь-які зміни місця розташування системних
файлів зажадають додаткових змін де-небудь ще.
Більш ефективним вирішенням цієї проблеми є застосування директиви
'# include_next', яка використовується для підключення наступного файлу з
таким же ім'ям. Ця директива функціонує також как и директива '# include'
за винятком пошуку необхідного файлу. Вона починає пошук списку каталогів
підключаються файлів після каталогу, де був знайдений поточний файл.
Припустимо була вказана опція '-I/usr/local/include', а список
каталогів для пошуку включає '/ usr/include'. Також припустимо, що обидва
каталогу містять файл з назвою 'sys/signal.h'. Директива
'# include' знайде потрібний файл з каталогом
'/ usr/local/include'. Якщо цей файл містить рядок
'# include_next', то пошук буде відновлено після попереднього
каталозі і буде знайдений файл у каталозі '/ usr/include'.
4. Макроси
Макрос це тип скорочення, який можна заздалегідь визначити і
використовувати надалі. Існує досить багато можливостей, пов'язаних
з використанням макросів в С препроцесор.
4.1. Прості макроси
"Простий макрос" це тип скорочення. Це ідентифікатор, який
використовується для представлення фрагмента коду.
Перед використанням макросу його необхідно визначити за допомогою
директиви '# define', за якою йде назва макросу і фрагмент коду,
який буде ідентифікувати цей макрос. Наприклад,
# define BUFFER_SIZE 1020
визначає макрос з назвою 'BUFFER_SIZE', якому відповідає текст
'1024 '. Якщо де-небудь після цієї директиви зустрінеться вираження в наступній
формі:
foo = (char *) xmalloc (BUFFER_SIZE);
то З препроцесор визначить і замінить макрос 'BUFFER_SIZE' на його значення і
в результаті вийде
foo = (char *) xmalloc (1020);
Використання великих літер у назвах макросів є стандартним
угодою і підвищує читабельність програм.
Зазвичай, макровизначеннями має бути окремий рядок, як і при
використанні всіх директив препроцесора. (Довге макровизначеннями можна
розбити на кілька рядків із застосуванням послідовності
backslash-newline.) Хоча існує одне виключення: символи перекладу
рядки можуть бути вкючени в макровизначеннями якщо вони перебувають у послідовну
або символьної константи, тому як макровизначеннями не може містити
будь-яких спеціальних символів. Макровизначеннями автоматично доповнюється
відповідним спеціальним символом, який завершує рядкову або
символьну константу. Коментарі в макровизначеннями можуть містити символи
переведення рядка, так як це ні на що не впливає, тому як всі коментарі
повністю замінюються пробілами незалежно від того, що вони містять.
На відміну від вище сказаного, не існує ніяких обмежень на
значення макросу. Дужки не обов'язково повинні закриватися. Тіло макросу не
обов'язково має містити правильний З код.
Препроцесор З обробляє програму послідовно, тому
макровизначеннями вступають в силу тільки в місцях, де вони використовуються.
Тому, після обробки наступних даних З препроцесором
foo = X;
# define X 4
bar = X;
вийде такий результат
foo = X;
bar = 4;
Після підстановки препроцесором імені макросу, тіло макровизначеннями
додається до початку залишилися даних, що вводяться і відбувається перевірка на
продовження викликів макросів. Тому тіло макросу може містити посилання
на інші макроси. Наприклад, після виконання
# define BUFSIZE 1020
# define TABLESIZE BUFSIZE
значенням макросу 'TABLESIZE' стане в результаті значення '1020 '.
Це не є тим же, що і визначення макросу 'TABLESIZE' рівним
значенням '1020 '. Директива '# define' для макросу 'TABLESIZE' використовує в
точності ті дані, які були зазначені в її тілі і замінює макрос
'BUFSIZE' на його значення.
4.2. Макроси з аргументами
Значення простого макросу завжди одне і те ж при кожному його
використанні. Макроси можуть бути більш гнучкими, якщо вони беруть
аргументи. Аргументами є фрагменти коду, які додаються при
кожному використанні макросу. Ці фрагменти включаються в розширення макросу
відповідно до вказівок в макровизначеннями.
Для визначення макросу, який використовує аргументи, застосовується директива
'# define' зі списком імен аргументів в дужках після імені макросу. Іменами
аргументів можуть бути будь-які правильні З ідентифікатори, розділені комами
і, можливо, пробілами. Відкривається, дужка повинна слідувати відразу ж після
імені макросу без будь-яких пропусків.
Наприклад, для обчислення мінімального значення з двох заданих можна
використовувати наступний макрос:
# define min (X, Y) ((X) <(Y)? (X): (Y))>
Для застосування макросу з аргументами потрібно вказати ім'я макросу, за
яким слідує список аргументів, укладених у дужки і розділених
комами. Кількість прийнятих аргументів повинна відповідати кількості
вказуються. Наприклад, макрос 'min' можна використовувати так: 'min (1, 2)' або
'min (x + 28, * p)'.
Значення макросу залежить від використовуваних аргументів. Кожне ім'я
аргументу в усьому макровизначеннями замінюється на значення відповідних
зазначених аргументів. При використанні макросу 'min', розглянутого раніше,
наступним чином:
min (1, 2)
буде отриманий такий результат:
((1) <(2)? (1): (2))>
де '1 'замінює' X ', а '2' замінює 'Y'.
При вказівці аргументів, дужки повинні закриватися, а кома не повинна
завершувати аргумент. Однак, не існує будь-яких обмежень на
використання квадратних або кутових дужок. Наприклад
macro (array [x = y, x + 1])
передає макросу 'macro' два аргументи: 'array [x = y' і 'x + 1]'.
Після підстановки зазначених аргументів на тіло макросу, отриманий в
результаті текст додається до початку залишилися даних і виробляється
перевірка на наявність інших викликів макросів. Тому вказані аргументи
можуть містити посилання до інших макросу як з аргументами, так і без, а
також до того ж макросу. Тіло макросу також може включати посилання до інших
макросу. Наприклад, макрос 'min (min (a, b), c)' замінюється наступним
текстом:
((((a) <(b)? (a): (b))) <(c)>
? (((a) <(b)? (a): (b )))>< br />
: (C))
(Терміну розбита на три для ясності і насправді вона не розбивається.)
Якщо макрос 'foo' приймає один аргумент і потрібно передати йому порожній
аргумент, то в дужках слід вказати принаймні один пробіл:
'foo ()'. Якщо пробіл не вказувати, а макрос 'foo' вимагає один аргумент,
то станеться помилка. Для виклику макросу, не приймає аргументи, можна
використовувати конструкцію 'foo0 ()' як розглянуто нижче:
# define foo0 ()...< br />
Якщо використовується ім'я макросу, за яким не слід відкривається
дужка (після видалення всіх наступних пробілів, символів табуляції і
коментарів), то це не є викликом макросу і препроцесор не змінює
текст програми. Тому можливе використання макросу, змінної та функції
з одним ім'ям і в кожному випадку можна змінювати, коли потрібно застосувати макрос
(якщо за ім'ям слід список аргументів), а коли - змінну або функцію
(якщо список аргументів відсутня).
Подібне подвійне використання одного імені може призвести до
ускладнень і його слід уникати, за винятком випадків, коли обидва
значення є синонімами, тобто коли під одним ім'ям визначена
функція і макрос і обидва виконують однакові дії. Можна розглядати
це ім'я як ім'я функції. Використання імені не для посилання (наприклад, для
отримання адреси) приведе до виклику функції, в той час як посилання приведе
до заміни імені на значення макросу і в результаті буде отримано більше
ефективний але ідентичний код. Наприклад, використовується функція з ім'ям
'min' в тому ж вихідному файлі, де визначений макрос з тим же ім'ям.
Якщо написати '& min' без списку аргументів, то це призведе до виклику функції.
Якщо ж написати 'min (x, bb)' зі списком аргументів, то замість цього буде
замінено на значення відповідного макросу. Якщо використовувати
конструкцію '(min) (a, bb)', де за назвою 'min' не слід відкривається
дужка, то буде зроблений виклик функції 'min'.
Не можна визначати простий макрос і макрос з аргументами з одним ім'ям.
У визначенні макросу з аргументами список аргументів повинен слідувати
відразу після імені макросу без пробілів. Якщо після імені макросу варто
пробіл, то макрос визначається без аргументів, а інша частина рядків
стає значенням макросу. Причиною цьому є те, що досить
часто визначаються макроси без аргументів. Визначення макросів подібним
чином дозволяє виконувати такі операції, як
# define FOO (x) - 1/(x)
(де визначається макрос 'FOO', що приймає один аргумент і додає мінус
до числа, зворотному аргументу) або
# define BAR (x) - 1/(x)
(де визначається макрос 'BAR' без аргументів і має постійне значення
'(x) - 1/(x )').< br />
4.3. Заздалегідь визначені макроси
Деякі прості макроси є заздалегідь визначеними. Їх можна
застосовувати без попереднього визначення. Вони поділяються на два класи:
стандартні макроси і системно-залежні макроси.
4.3.1. Стандартні заздалегідь визначені макроси
Стандартні заздалегідь визначені макроси можуть застосовуватися поза
залежно від використовуваної платформи або операційної системи на якій
функціонує GNU C. Їх імена починаються і закінчуються подвійним символом
підкреслення. Усі макроси в наступному списку до '__GNUC__' є
стандартизованими ANSI C. Решта макроси є розширеннями GNU C.
'__FILE__'
Цей макрос замінюється на ім'я поточного вихідного файлу у формі рядковий
константи С. повертаються ім'ям є одне із зазначених у директиві
'# include' або ім'я основного вихідного файлу.
'__LINE__'
Цей макрос замінюється на номер поточного рядка у формі десяткового цілої
константи. У той час як він називається заздалегідь визначених макросом, його
значення змінюється динамічно.
Цей макрос і макрос '__FILE__' використовуються при генеруванні повідомлення
про помилку для виведення невідповідності, визначеного програмою. Повідомлення
може містити номер рядка вихідного файлу де була виявлена помилка.
Наприклад,
fprintf (stderr, "Internal error:"
"negative string length"
"% d at% s, line% d.",
length, __FILE__, __LINE__);
Директива '# include' змінює значення макросів '__FILE__' і '__LINE__'
на відповідні вихідного файлу. Наприкінці цього файлу, якщо це був
підключається файл, значення '__FILE__' і '__LINE__' стають тими, якими
вони були до директиви '# include' (тільки значення '__LINE__' збільшується
на одиницю, тому що потім обробляється рядок, наступна за директивою
'# include').
Значення '__FILE__' і '__LINE__' змінюються при використанні директиви
'# line'.
'__DATE__'
Цей макрос замінюється на рядкову константу, яка вказує дату
запуску препроцесора. Ця константа містить одинадцять символів і
виглядає приблизно так ' "Jan 29 1987"' або ' "Apr 1 1905"'.
'__TIME__'
Цей макрос замінюється на строкову константу, яка вказує час
запуску препроцесора. Константа містить вісім символів і виглядає
приблизно так: ' "23:59:01:'.
'__STDC__'
Цей макрос замінюється на константу зі значенням 1 для зазначення, що
це З стандарту ANSI.
'__STDC_VERSION__'
Цей макрос замінюється на номер версії стандарту З, довжиною цілої
константою у формі "YYYYMML ', де YYYY і MM рік і місяць виходу версії
стандарту. Це вказує на версію стандарту С, до якої належить
препроцесор.
'__GNUC__'
Цей макрос визначений тоді і тільки тоді, коли використовується GNU C.
Він визначений тільки тоді використовується повний GNU C компілятор. Якщо
викликати препроцесор окремо, то цей макрос буде не визначений. Його
значення вказує на основний номер версії GNU CC ('1 'для версії 1 GNU CC,
яка вже є застарілою, і '2 'для версії 2).
'__GNUC_MINOR__'
Цей макрос містить додатковий номер версії компілятора. Він може
бути використаний при роботі з відмінними можливостями різних випусків
компілятора.
'__GNUG__'
Компілятор GNU C визначає цей макрос якщо компільовані мовою
є С + +.
'__cplusplus'
Стандарт ANSI для C + + раніше вимагав визначення цієї змінної.
Хоча її наявність більше не потрібно, в GNU C + + вона все ще визначається, як
і в інших відомих компіляторах С + +. Цей макрос може бути використаний
для визначення яким компілятором було скомпільовано заголовок (С або З ++).< br />
'__STRICT_ANSI__'
Цей макрос визначається тоді і тільки тоді, коли при виклику GNU C
вказується опція '-ansi'. Він визначається як порожній рядок.
'__BASE_FILE__'
Цей макрос замінюється на ім'я основного вихідного файлу у формі
строковий константи С. Це початковий файл, що указується як параметр
при виклику компілятора С.
'__INCLUDE_LEVEL__'
Цей макрос замінюється на десяткову цілу константу, яка вказує
на рівень вкладеності підключаються файлів. Його значення збільшується на
одиницю при обробці директиви '# include' і зменшується на одиницю при
завершення обробки кожного файлу. Початкове значення для файлів,
згаданих в командному рядку при виклику компілятора є рівним нулю.
'__VERSION__'
Цей макрос замінюється сторокой, що вказує номер версії GNU C.
Зазвичай це послідовність десяткових чисел, розділених крапками.
Наприклад ' "2.6.0"'.
'__OPTIMIZE__'
Цей макрос визначається в оптимізують компіляторах. Якщо але
визначений, то це призводить до створення в підключаються файлах GNU
альтернативних макроозначень для деяких функцій з системних бібліотек.
Перевірка або використання значення цього макросу не має особливого сенсу,
до тих пір, поки не буде повної впевненості в тому, що програми будуть
виконуватися з таким же ефектом.
'__CHAR_UNSIGNED__'
Цей макрос визначається тоді і тільки тоді, коли тип даних 'char'
є беззнакові. Він реалізований для правильного функціонування
підключається файлу 'limit.h'. Не слід використовувати цей макрос. Замість
цього можна використовувати стандартні макроси, визначені у файлі 'limit.h'.
Препроцесор використовує цей макрос для визначення необхідності в
додаванні знакового біта у великих вісімкових символьних константах.
'__REGISTER_PREFIX__'
Цей макрос замінюється на терміну, що описує префікс, що додається до
позначенню регістрів процесора в асемблерні коді. Він може використовуватися
для написання асемблерні коду, яке функціонує в різних оболонках.
Наприклад, в оболонці 'm68k-aout' проводиться заміна на рядок'""', а
в оболонці 'm68k-coff' макрос замінюється на рядок'"%"'.< br />
'__USER_LABEL_PREFIX__'
Цей макрос замінюється на рядок, що описує префікс, що додається до
меткам користувача в асемблерні коді. Він може використовуватися для
написання асемблерні коду, яке функціонує в різних оболонках.
Наприклад, в оболонці 'm68k-out' він замінюється на рядок ' ""', а в оболонці
'm68k-coff' - на рядок'""'.< br />
4.3.2. Нестандартні заздалегідь визначені макроси
Зазвичай З препроцесор має кілька заздалегідь певних макросів,
значення яких різняться в залежності від використовуваної платформи і
операційної системи. У цьому посібнику не представляється можливим
розглянути всі макроси. Тут описані лише найбільш типові з них.
Для перегляду значень заздалегідь певних макросів можна скористатися
командою 'cpp-dM'.
Деякі нестандартні заздалегідь визначені макроси більш-менш
детально описують тип операційної системи. Наприклад,
'unix'
Цей макрос звичайно визначений на всіх системах Unix.
'BSD'
Цей макрос визначений на останніх версіях системи Berkley Unix
(можливо тільки у версії 4.3).
Інші макроси описують тип центрального процесора. Наприклад,
'vax'
Визначено на Vax комп'ютерах.
'mc68000'
Визначено на більшості комп'ютерів, що використовують процесор Motorola
68000, 68010 або 68020.
'm68k'
Також визначено на більшості комп'ютерів з процесором 68000, 68010
або 68020. Хоча деякі розробники використовують 'mc68000', а деякі -
'm68k'. Деякі заздалегідь визначають обидва макросу.
'M68020'
Визначено на деяких системах із процесором 68020 на додаток до
макросу 'mc68000' і 'm68k', які є менш специфічними.
'_AM29K'
'_AM29000'
Визначено на комп'ютерах з процесорами з сімейства AMD 29000.
'ns32000'
Визначено на комп'ютерах, що використовують процесори серії National
Semiconductor 32000.
Інші нестандартні макроси описують виробників комп'ютерних
систем. Наприклад,
'sun'
Визначено на всіх моделях комп'ютерів Sun.
'pyr'
Визначено на всіх моделях комп'ютерів Pyramid.
'sequent'
Визначено на всіх моделях комп'ютерів Sequent.
Ці заздалегідь визначені символи є не тільки нестандартними, але
вони до того ж не відповідають стандарту ANSI, тому що їхні імена не
починаються з символу підкреслення. Тому опція '-ansi' забороняє
визначення цих символів.
Це призводить до того, що опція '-ansi' стає даремною, тому що
велика кількість програм залежить від нестандартних заздалегідь визначених
символів. Навіть системні подключамие файли перевіряють їх значення і
генерують неправильні оголошення у випадку якщо необхідні імена не
визначені. В результаті дуже мало програм компілюється з опцією '-ansi'.
Що ж потрібно зробити в ANSI C програмі для того, щоб перевірити тип
використовуваного комп'ютера?
Для цієї мети GNU C надає паралельну серію символів, імена
яких складаються зі звичайних символів з додаванням рядка '__' з початку і
з кінця. Таким чином символ '__vax__' використовується на системах Vax, і
так далі.
Набір нестандартних заздалегідь певних символів у GNU C препроцесор
змінюється (при компіляції самого компілятора) за допомогою макросу
'CPP_PREDEFINES', яким є рядок, що складається з опцій '-D',
розділених пробілами. Наприклад, на системі Sun 3 використовується наступне
макровизначеннями:
# define CPP_PREDEFINES "-Dmc68000-Dsun-Dunix-Dm68k"
Цей макрос зазвичай вказується у файлі 'tm.h'.
4.4. Стрінгіфікація
"Стрінгіфікація" означає перетворення фрагмента коду в строкову
константу, яка містить текст цього фрагмента коду. Наприклад, у результаті
стрінгіфікаціі 'foo (z)' виходить ' "foo (z )"'.< br />
У З препроцесора, стрінгіфікація є опцією, що використовується при
заміні аргументів на макросі макровизначеннями. При появі імені аргументу
в тілі макровизначеннями, символ '#' перед ім'ям аргументу вказує на
стрінгіфікацію відповідного аргументу коли його підстановці в цьому місці
макровизначеннями. Цей же аргумент може бути замінений в іншому місці
макровизначеннями без його стрінгіфікаціі, якщо перед ім'ям аргументу немає
символу'#'.< br />
Ось приклад макровизначеннями з використанням стрінгіфікаціі:
# define WARN_IF (EXP)
do (if (EXP)
fprintf (stderr, "Warning:" # EXP "n");)
while (0)
Тут аргумент 'EXP' замінюється один раз звичайним чином (в конструкції
'if'), а інший - з використанням стрінгіфікаціі (аргумет функції
'fprintf'). Конструкція 'do' і 'while (0)' є реалізацією макросу
'WARN_IF (ARG );'.< br />
Можливості срінгіфікаціі обмежені до перетворення одного макро
аргументу в одну строкову константу: не з