Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Использование директивы #define




С помощью директивы #define можно вводить собственные обозначения базовых или производных типов.

Пример:

Директива

#define REAL long double

вводит имя REAL для типа long double. Далее в тек­сте программы можно определять объекты типа long double, используя данное имя:

REAL x, array [6];

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

Директиву #define также удобно использовать для сокращенного обозначения оператора печати, в случае, когда в программе требуется часто выводить на эк­ран значение переменной с одним и тем же пояснительным текстом.

Пример:

После записи директивы

#define PN printf ("\n Номер элемента=%d", N);

Последовательность операторов

int N=4;

PN;

выведет на экран текст

Номер элемента=4

Еще одной из областей эффективного применения макросов является адресации элементов многомерных массивов.

Доступ к элементам многомерных массивов в С++ имеет две особенности, которые создают неудобства при работе с ними:

· при обращении к элементу массива нужно указывать все его индексы,

· нумерация элементов массивов начинается с нуля.

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

Пример:

#define N 4 // число строк матрицы

#define M 5 // число столбцов матрицы

#define A(i,j) x[M*(i-l) + (j-1)]

#include <stdio.h>

void main ()

{

/* Определение одномерного массива */

double x[N*M];

int i, j, k;

for (k=0; k < N*M; k++) x[k]=k;

/* Перебор строк */

for (i=1; i<=N; i++) {

printf ("\n Строка %d:", i);

for (j=1; j<=M; j++) printf(" %6.1f", A(i, j)); // перебор элементов строк

}

}

Результат выполнения программы:

Строка 1: 0.0 1.0 2.0 3.0 4.0

Строка 2: 5.0 6.0 7.0 8.0 9.0

Строка 3: 10.0 11.0 12.0 13.0 14.0

Строка 4: 15.0 16.0 17.0 18.0 19.0

В программе создается виртуальный многомерный массив, размерностью N×М (N – число строк, M - число столбцов). Размерность массива задается на этапе пре­процессорной обработки с помощью директивы #define. Для создания виртуального многомерного массива в программе используется макроопределения и одномерный (реальный) массив x[] размерностью N*M. Таким образом, элементы виртуального многомерного массив будут размешаться в одномерном массиве построчно. Значения элементам массива присваиваются в цикле с параметром k.

На рисунке 1.11 приведена схема одномерного массива х[ ] для моделирования виртуального двумерного массива с помощью макоопределений.

Рисунок 1.11 – Имитация многомерного массива с помощью одномерного массива и макроопределения.

Для доступа к элементам массива используются макровызо­вы A(i, j). Индекс i соот­ветствует номеру строки многомерного массива и изменяется от 1 до N, а индекс j – номеру столбца и изменяется во внутреннем цикле от 1 до М. A(i,j) является достаточно естественным обо­значениями элементов матрицы, причем нумерация столбцов и строк начинается с 1.

За счет применения макросов выполняются замены параметризо­ванных обозначений A(i, j) на x[5*(i-l)+(j-l)]. Далее действия выполняются над элементами одномерного массива х[ ], но т.к. данные преобразования выполняются на этапе препроцессорной обработки можно считать, что осуществляется работа с традиционными для многомерных массивов обозначениями.

 

Использованный в программе оператор

printf (“% 6.1f”, A (i, j));

после макроподстановок будет иметь вид:

printf (“% 6.1f”, x[5*(i-l)+(j-l)]);

Например:

A (1,1) соответствует x[0] - вычислено как x[5(1-1)+(1-1)]

A (1,2) соответствует x[1] – вычислено x[5(1-1)+(2-1)]

A (2,1) соответствует x[5] – вычислено как x[5(2-1)+(1-1)]

A (3,4) соответствует x[13] – вычислено как x[5(3-1)+(1-1)]

Макросы унаследованы из языка С, при написании программ на C++ их следует избегать. Вместо макросов без параметров предпочтительнее использовать const или enum, а вместо параметризованных макросов — встроенные функ­ции или шаблоны.

Директива #undef отменяет действие директивы #define.

Данная директива имеет следующий формат:

#undef идентификатор

После выполнения директивы идентификатор, ранее определенный директивой #define, для пре­процессора становится неопределенным, и его можно опреде­лять повторно.

Пример:

Если для переопределения константы M использовать последовательность директив

#define M 16

#undef M

#define М ‘С’

#undef M

#define M “С”

никаких предупреждающих сообщений выдано не будет (как это было в рассмотренном ранее примере без использования директивы #undef).

Директива #undefиспользуется редко, например, для отключения какой-либо опции компилятора. Также ее удобно использовать при разработке больших программ, собираемых из отдельных фрагментов текста, написанных в разное время или разными программистами, т.к. в этом случае могут встретиться одинаковые обозначе­ния различных объектов. Чтобы не изменять исходных файлов, включаемый текст можно обрамлять директи­вами #define, #undef и тем самым устранять возможные ошиб­ки.

Пример:

А = 10; // Основной текст

#define A X

А = 5; // Включенный текст

#undef A

В = А; // Основной текст

При выполнении программы переменная В примет значение 10, несмотря на наличие оператора присваивания А = 5; во включенном тексте.

Директива #include вставляет в текст программы описания из указанного файла, в ту точку, где эта директива записана.

Данная директива имеет две формы записи:

#include <имя_файла>

#include “имя_файла”

Конструкция, указывающая имя файла, может являться вызовом макроса, введенного директивой #define, который за конечное число подстановок формирует последовательность символов <имя_файла> либо “имя файла”.

Имя файла может быть указано с расширением. Файлы с расширением.h, называются заголовочными файлами (header file). Они могут содержать:

· определения типов, констант, встроенных функций, шаблонов, перечислений;

· объявления функций, данных, имен, шаблонов;

· пространства имен;

· директивы препроцессора;

· комментарии.

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

В форме заголовочных файлов оформляются описания функций стандартных библиотек, а также определения и описания типов и констант, используе­мых при работе с библиотеками компилятора. Например, заголовочный файл stdio.h содержит описание функции ввода/вывода printf, scanf и др. Каталог заголовочных файлов поставляется вместе со стандартными библиотеками компилятора.

Для подключения стандартных заголовочных файлов, используется первая форма записи (имя заключается в угловые скобки). В этом случае поиск файла ведется в стандартных каталогах заголовочных файлов. Например, для включения в текст программы заголовочного файла stdio.h используется директива

#include < stdio.h >.

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

Перечень заголовочных файлов утвержден стандартом языка. Перечислим некоторые заголовочные файлы языка С++:

float.h - работа с вещественными данными

limits.h - предельные значения целочисленных данных

math.h - математические вычисления

stdio.h - средства ввода-вывода

string.h - работа со строками символов

time.h - определение дат и времени

В конкретных реализациях состав и наименования заголовочных файлов могут отличаться. Например, в компиляторах для MS-DOS активно используются файлы mem.h, alloc.h, conio.h, dos.h, graphics.h и др.

Для каждого файла библиотеки С с именем <name.h> имеется соответст­вующий файл библиотеки C++ <cname>, в котором те же средства описываются в пространстве имен std. Например, директива #include <cstdio> обеспечивает те же возможности, что и #include <stdio.h>, но при обращении к стандартным функциям требуется указывать имя пространства имен std.

Программист может самостоятельно создать собственные заголовочные файлы. При этом используется вторая форма записи (имя файла указывается в кавычках). В этом случае поиск файла ведется сначала в каталоге с исходным файлом, а затем в стандартных каталогах.

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

Внешние объекты должны записываться в заголовочном файле со спецификатором extern. Например:

extern int ii, jj, ll; // целые внешние переменные

extern float aa, bb; // вещественные внешние переменные

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

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

К директивам условной компиляции относятся директивы #if, #ifdef, #ifndef, #else, #endif, #elif. Они позволяют организовать услов­ную (в зависимости от результата выполнения некоторого условия) препроцессорную обработку текста программы, позволяя исключить из процесса компиляции сключить часть текста программы. Поэтому данные директивы и называются директивами условной компиляции, хотя, как и все директивы препроцессора, они управляют препроцессорной обработкой текста программыдо ее компиляции.

Директивы #if, #ifdef, #ifndef выполняют проверку условий.

Общая структура их применения имеет следующий формат:

#if | #ifdef | #ifndef условие

текст_1

[#else

текст_2]

#endif

Директива #endif указывает окончание действия директив #if, #ifdef и #ifndef.

Текст_1вклю­чается в компилируемый текст при истинности прове­ряемого условия. Директива #else определяет начало альтернативной ветви и не является обязательной. Если условие ложно, то при ее наличии ветви #else в компилируемый текст включается текст_2, при ее отсутствии весь текст от #if до #endif опуска­ется.

Различие между директивами #if, #ifdef и #ifndef состоит в типе проверяемого условия. Директива #if задает проверку условия-выражения, директива #ifdef - проверку определенности идентификатора, #ifndef - проверка неопределенности идентификатора.

Директива #if имеет формат

#if выражение

Выражение может содержать целые константы и идентификаторы. Если идентификаторы определены на препроцессорном уровне, их значение определяется макроподстановками, в противном случае они имеет нулевые значения. Проверяемое условие истинно, если константное выражение отлично от нуля.

Пример:

В результате препроцессорной обработки директив:

#if 5+4

текст_1

#endif

текст_1всегда будет включен в компилируемую программу.

Данная директива может использоваться для того, чтобы временно закомментировать фрагменты кода.

Пример:

#if 0

int i, j;

double x, y;

#endif

Директива #ifdef имеет формат

#ifdef идентификатор

Проверяемое условие истинно, если идентификатор является препроцессорным, т.е. ранее определен директивой #define.

Директива #ifndef имеет формат

#ifndef идентификатор

Проверяемое условие истинно, если идентификатор не является препроцессорным, т.е. ранее не определен директивой #defineили его определение бы­ло отменено директивой #undef.

Определение идентификатора, управляющего условной компиляцией, осуществляется с помощью третьей модификации директивы #define:

#define идентификатор

Строка замещения в данном случае отсутствует.

Директиву #ifdef удобно применять при отладке про­грамм для включения или исключения средств вывода кон­трольных сообщений.

Пример:

После определения идентификатора DEBUG с помощью директивы

#define DEBUG

В программе можно использовать для вывода контрольного сообщения конструкцию

#ifdef DEBUG

printf (“Отладочная печать”);

#endif

Оператор printf (“Отладочная печать”); выполняется, т.к. идентификатор DEBUG ранее определен. Вывод контрольного сообщения может использоваться программе неоднократно, но убрав либо поместив в скобки коммен­тария директиву #define DEBUG, можно отключить их все сразу.

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

Пример:

#ifndef _FILE_NAME // проверка определенности _FILE_NAME

#include "filename.h" // включение текста файла filename.h

#define _FILE_NAME // определение _FILE_NAME

#endif

Здесь _FILE_NAME - зарезервированный для файла с именем filename препроцессорный идентификатор. Его нежелательно ис­пользовать в других текстах программы.

Директива #elif является составной директивой #else - #if. Она используется для организации множественных ветвлений.

Данная директива имеет формат

#elif выражение

Требования к выражению такие же, как и для директивы #if.

Формат применения данной директивы:

#if выражение_0

текст_1

#elif выражение_1

текст_2

#elif выражение_2

текст_3

#else

текст_для__еlse

#endif

Количество директив #elif является произвольным.

Препроцессор сначала проверяет выражение_0 в директиве #if. Если оно не равно 0, в компилируемый текст включается текст_1f, ес­ли оно равно 0, вычисляется выражение_1. Если выражение_1 не равно 0 в текст включается текст_2, если оно равно 0, вычисляется выражение_2 и т.д. Если все выражения равны 0, то в компилируемый текст включается текст_для_else.

При появлении ненулевого выражения в одной из директив (#if или #elif) в компилируемый текст включается текст, расположенный после данной директивы, а все остальные директивы не рассмат­риваются. Таким образом, в компилируемый текст включается всегда только один из участков текста, выделенных директивами условной компиляции. Это бывает полез­но при отладке или, например, при поддержке нескольких версий программы для различных платформ.

Пример:

#if VERSION == 1

#define INCFILE "versl.h"

#elif VERSION ==2

#define INCFILE "vers2.h"

#else

#define INCFILE "vers3.h"

#endif

#include INCFILE

В данном примере выполняется вклю­чения различных версий заголовочного файла.

Операция defined

При использовании директив #if и #elifдля упрощения записи сложного условия выбора можно использо­вать унарную препроцессорную операцию defined. Она имеет формат:

defined операнд

В качестве операнда может выступать идентификатор, заключенный в скобки идентификатор и вызов макроса. Выражение принимает значение 1, если идентификатор является препроцессорным (ранее определен директивой #define), в противном случае - значение 0.

Выражение

#if defined операнд

эквивалентно выражению

#ifdef операнд

При рассмотрении такого простого случая достоинства операции definedне проявляются. С помощью следующего примера можно пояснить полезные возможности операции defined.

Пример:

#if defined Y &&!defined N

текст

#endif

Текст включается в компилируемый текст только в том случае, если идентификатор Y определен как препроцес­сорный, а идентификатор N не определен.

Обработку препроцессор ведет следующим образом. Сначала, определяется истинность выражений defined Y и!defined N. К резуль­татам применяется операция конъюнкции &&, и при истинно­сти ее результата текст передается компилятору.

Не используя операцию defined, то же самое условие можно записать таким способом:

#ifdef Y

#ifndef N

текст

#endif

#endif

Таким образом, из примера видно, что

#if defined эквивалентно #ifdef

#if!defined эквивалентно #ifndef

Стандарт языка С++ не определил defined в качестве ключе­вого слова. В тексте программы его можно использовать в каче­стве идентификатора. Специфическое значение имеет defined только при формировании условий, проверяе­мых в директивах #if и #elif. Запрещено использовать defined в директивах #define и #undef.

Вспомогательные директивы

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

Директива #line позволяет управлять нумерацией строк в файле с программой, а также изменять имя файла.

После препроцессорной обработки каждая строка текста передаваемого на компиляцию имеет следующий вид:

имя_файла номер_строки: текст_на_языке_С++

Директива #line имеет следующий формат:

#line номер_строки [“имя_файла”]

Данная директива позволяет изменять номер и имя файла следующей за ней строки на указанные значения.

Пример: препроцессор получает для обработки файл "www.c" с текстом:

#define N 3 // строка 1

void main () // строка 2

{ // строка 3

#line 23 "file.с" // строка 4

double z[3*N]; // строка 5

} // строка 6

После препроцессорной обработки в файле с именем "www.i" будет получен следующий набор строк:

www.c 1:

www.c 2: void main ()

www.c 3: {

www.c 4:

file.c 23: double z[3*3]

file.c 24: }

Из текста исключены препроцессорные директивы и комментарии. При этом строки, где размещались директивы препроцессора (1 и 4), вошли в компилируемый текст, как пустые строки. Следующей за ди­рективой #line строке присвоен номер 23 и имя файла "file.c" в соответствие с указанными значениями. Следующая строка имеет такое же имя файла и номер строки на единицу больше.

Директива #line может использоваться в случае, когда текст программы на языке С++ генерирует какой-то другой препроцессор.

Директива #error задает текст сообщения, которое выводится при возникновении ошибок компиляции.

Она имеет следующий формат:

#error текст_сообщения

Выполнение данной директивы приводит к выдаче диагностического сообщения, содержащего указанный текст. Текст может содержать препроцессорные идентификаторы. Директива #error может применяться совместно с директивами условной компиляции.

Пример:

Определив некоторую препроцессорную переменную NAME

#define NAME 5

можно проверять, не изменилось ли ее значение, и выдавать в этом случае сообще­ние:

#if (NAME!= 5)

#error NAME должно быть равно 5

В интегрированной среде (например, Turbo С) сообщение будет выдано на этапе компиляции в виде:

Fatal <имя_файла> <номер_строки>:

Error directive: NAME должно быть равно 5

В случае выявления такой аварийной ситуации дальнейшая препроцессорная обработка исходного текста прекратиться, и в компилируемый текст попадет только та часть текста, которая предшествует директиве #if.

Директива #pragma вызывает действия, зависящие от конкретной реализации компилятора.

Она имеет следующий формат

#pragma последовательность _лексем

Данная конструкция называется прагмой. Стандарта для прагм не существует. Если конкретный препроцессор встречает прагму, которая ему неиз­вестна, он ее игнорирует.

В неко­торых реализациях включена директива

#pragma pack(n)

n может быть равно 1, 2 или 4.

Данная прагма позволяет влиять на упаковку смежных эле­ментов в структурах и объединениях. Соглашение может быть таким:

pack(l) - выравнивание элементов по границам байтов;

расk(2) - выравнивание элементов по границам слов;

расk(4) - выравнивание элементов по границам двойных слов.

В некоторые компиляторы включены прагмы, позволяющие изменять способ передачи параметров функциям, порядок по­мещения параметров в стек, сообщать компилятору о наличии в тексте программы команд на языке ассемблера и т.д.

Существует также пустая директива, использование которой не вызывает никаких действий (она игнорируется). Она имеет вид: #.


РАЗДЕЛ 2





Поделиться с друзьями:


Дата добавления: 2016-11-12; Мы поможем в написании ваших работ!; просмотров: 1339 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Бутерброд по-студенчески - кусок черного хлеба, а на него кусок белого. © Неизвестно
==> читать все изречения...

2414 - | 2335 -


© 2015-2024 lektsii.org - Контакты - Последнее добавление

Ген: 0.015 с.