Пространства имен стандартной библиотеки.

Объекты стандартной библиотеки определены в пространстве имен std.Например, объявления стандартных средств ввода/вывода С в заголовочном файле <stdio.h>помещены в пространство имен следующим образом:

namespace std{

int feof (FILE *f);

}

using namespace std;

Если нежелательно присутствие неявно доступных имен, необходимо использовать заголовочный файл cstdio, где пространство имен std определено следующим образом:

namespace std{

int feof(FILE * f);

}

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

std::feof(f);

Тема 1.24

Директивы препроцессора

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

Препроцессор может обрабатывать не только тексты программ на языке С++, а также произвольные тексты.

Стадии препроцессорной обработки

Препроцессорная обработка включает несколько последовательно выполняемых стадий (конкретная реализация может объединять несколько стадий в одну):

1. все системно-зависимые обозначения (например, индикатор конца строки) перекодируются в стандартные коды,

2. каждая пара из символов ‘\’ и “конец строки” вместе с пробелами между ними убираются, и тем самым соседние строки конкатенируются, т.е. соединяются в одну строку (далее каждая полученная таким образом строка обрабатывается препроцессором отдельно),

3. в каждой строке распознаются директивы и лексемы препроцессора, а каждый комментарий заменяется одним символом пустого промежутка,

4. выполняются директивы препроцессора и производятся макроподстановки,

5. escape-последовательности в символьных константах и строках (например ‘\n’ или ‘\xF2’'), заменяются соответствующими числовыми кодами,

6. смежные символьные константы и строки кон­катенируются,

7. каждая препроцессорная лексема преобразуется в текст на языке С++.

К препроцесорным лексемам (лексемам препроцессора, preprocessing token) относятся:

· символьные константы,

· строковые константы,

· имена заголовочных файлов,

· идентификаторы,

· знаки операций,

· препроцессорные числа,

· знаки препинания,

· другие символы, отличные от пробела.

Директивы препроцессора

Для управления препроцессорной обработкой (для задания нужных действий), используются директивы (команды) препроцессора.

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

· замена идентификаторов определенной последовательностью символов,

· включение в программу текстов из заголовочных файлов,

· исключение из программы отдельных частей ее текста (условная компиляция),

· макроподстановка, т.е. замена обозначения параметризованным текстом, формируемым препроцессором с учетом конкретных параметров.

Обобщенный формат директивы препроцессора:

#имя_директивы лексемы_препроцессора

Каждая директива должна располагаться в отдельной строке. До и после символа ‘#’, до, после и между лексема­ми препроцессора могут содержаться пробелы.

Окончанием препроцессорной директивы служит конец текстовой строки, при наличии символа ‘\’, обозначающего перенос строки - конец следую­щей строки текста.

Определены следующие препроцессорные директивы: #define, #undef , #include, #if, #ifdef, #ifndef, #else, #endif, #elif, #line, #error, #pragma, #.

Кроме препроцессорных директив имеются три препроцессорные операции, используемые совместно с директивой #define: #, ##, defined.

Директива #define

Директива #define позволяет задать макроопределения. Замена в тексте программы одной последовательности символов на другую называется макроподстановкой. Средство реализации макроподстановки называется макросом. Для выполнения макроподстановок должны быть заданы соответствующие макроопределения.

Также с помощью директивы #define определяются символические константы, управляющие условной компиляцией, и используемые директивами #ifdef и #ifndef.

Директива имеет несколько модификаций:

· макроопределение без параметров,

· макроопределение с параметрами,

· определение идентификатора, управляющего условной компиляцией.

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

Макроопределение без параметров имеет вид:

#define идентификатор строка_замещения

Оно задает препроцессорный макрос без параметров. Идентификатор задает имя макроса, которое будет использоваться для обращения к нему.

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

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

Пример:

Исходный текст Результат препроцессорной обработки

#define begin {

#define end }

void main( ) void main( )

begin {

операторы операторы

end }

В исходном тексте программы в качестве операторных скобок используются идентификаторы begin, end.Препроцессор заменяет все вхождения данных идентификаторов скобками { и }.

Если строка замещения является слишком длинной, она может быть перенесена на следующую строку. Это можно осуществить, поместив символ ‘\’ в конце продолжаемой строки. В ходе второй стадии препроцессорной обработки этот символ вместе с последующим символом конца строки будет удален из текста программы.

Пример:

#define STRING "\n Game Over! - \

Игра закончена!"

printf (STRING);

На экран будет выведено:

Game Over! - Игра закончена!

В примере имя макроса записано прописными буквами. Данный прием позволяет зрительно отличать имена макросов от имен переменных и функций.

Действие макроподстановок, выполняемых согласно с директивой #define, имеет ряд ограничений. Замены не выполняются внутри:

· комментариев (строк, ограниченных символами /* и */ или записанных после знака //),

· символьных констант (символов, ограничен­ных апострофами '),

· строковых констант (строк, ограниченных двойными кавычками "),

· внутри идентификаторов (часть идентификатора не может быть заменена).

Пример:

#define n 24

char с = '\n'; // c - cимвольная константа, \n - escape-последовательность

c = ‘n’ > ‘\n’?'\n':'\n'

int k = n;

В ходе препроцессорной обработки данного текста замена идентификатора n на число 24 будет выполнена только один раз в последнем определении, которое примет вид:

int k=24;

Все остальные вхождения символа n в составе строковых констант препроцессором будут проигнорированы.

В строке замещения символы /*, */, //, ', " могут присутствовать (например, как в строке замещения макроса STRING).

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

#define M 16 // идентификатор М определен как 16

#define M ‘С’ // идентификатор М определен как символьная константа ‘С’

#define M “С” // идентификатор М определен как строковая константа

Однако при таких сменах значений препроцессорного иден­тификатора компилятор выдает предупреждающее сообщение на каждую следующую директиву #define:

Warning ..: Redefinition of 'M' is not identical

Цепочка подстановок

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

Пример: Определение диапазона возможных значений любой целой переменной типа int.

#include <limits.h>

#define RANGE ((INT_MAX) - (INT_MIN) +1)

/*RANGE - диапазон значений для int */

int RANGE_T = RANGE/8;

При работе препроцессора сначала выполняется директива #include <limits.h> - в текст программы вставляется текст из файла limits.h.

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

#define INT_MAX 32767

#define INT_MIN -32768

#define RANGE ((INT_MAX) - (INT_MIN) + 1)

/*RANGE - диапазон значений для int*/

int RANGE_T = RANGE/8;

Директива #includeв тексте программы заменилась на соответствующий текст (значения констант могут различаться в зависимости от разрядности ЭВМ).

Далее препроцессор выполняет подстановки в соответствии с двумя первыми директивами #define и программа принимает вид:

#define RANGE ((32767) - (-32768) + 1)

/*RANGE - диапазон Значений для int*/

int RANGE_T = RANGE/8;

В результате выполнения подстановок изменилась строка замещения макроса RANGE в последней директиве #define. После выполнения данной директивы текст программы примет вид:

/*RANGE - диапазон значений для int*/

int RANGE_T = ((32767)-(-32768)+1)/8;

Теперь все директивы #define удалены из текста.

Подстановка строки замещения вместо имени макроса RANGE была выполнена в выражении RANGE/8. Согласно рассмотренным ранее ограничениям не произошло подстановки внутри комментария, а также остался без изменений идентификатор RANGE_T.

Параметризованное макороопределение

Недостатком макроопределения без параметров является то, что замещения фиксиро­вана. Большие возможности предоставляет макроопределение с параметрами:

#define идентификатор(параметры) строка_замещения

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

Для макровызова использу­ется конструкция вида:

идентификатор (аргументы)

аргументы – список препроцессорных лексем, разделенных запятыми.

Пример:

Макроопределение

#define MAX(x,y) ((x) > (y) ? (x) : (y))

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

Аргументы макровызова используются при макроподстановке. Например, макровызов

у = MAX(suml, sum2);

он будет заменен на

у = ((suml)>(sum2)?(suml):(sum2));

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

Пример:

Если определен макрос

#define sqr(x) (x*x)

то макровызов sqr(y+l) будет заменен выражением sqr(у+1*у+1).

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

Наши рекомендации