Объекты стандартной библиотеки определены в пространстве имен 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.