SlideShare a Scribd company logo
1 of 16
Download to read offline
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
1
Лекция 13-4
Тема: Препроцессор языка Си.
Общие сведения
В интегрированную среду подготовки программ на Си или в компилятор
языка как обязательный компонент входит препроцессор. Назначение
препроцессора - обработка исходного текста программы до ее компиляции.
Препроцессорная обработка включает несколько стадий, выполняемых
последовательно. Конкретная реализация может объединять несколько стадий, но
результат должен быть таким, как если бы они выполнялись в следующем порядке:
Все системно-зависимые обозначения перекодируются в стандартные коды.
Каждая пара из символов '' и "конец строки" вместе с пробелами между ними
убираются, и тем самым следующая строка исходного текста присоединяется к
строке, в которой находилась эта пара символов.
В тексте распознаются директивы и лексемы препроцессора, а каждый
комментарий заменяется одним символом пустого промежутка.
Выполняются директивы препроцессора и производятся макроподстановки.
Эскейп-последовательности в символьных константах и символьных строках
заменяются на их эквиваленты.
Смежные символьные строки конкатинируются, то есть соединяются в одну
строку.
Каждая препроцессорная лексема преобразуется в текст на языке Си.
Поясним, что понимается под препроцессорными лексемами или лексемами
препроцессора. К ним относятся символьные константы, имена включаемых
файлов, идентификаторы, знаки операций, препроцессорные числа, знаки
препинания, строковые константы и любые символы, отличные от пробела.
Стадия обработки директив препроцессора. При ее выполнении возможны
следующие действия:
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
2
· замена идентификаторов заранее подготовленными
последовательностями символов;
· включение в программу текстов из указанных файлов;
· исключение из программы отдельных частей ее текста, условная
компиляция;
· макроподстановка, то есть замена обозначения параметризованным
текстом, формируемым препроцессором с учетом конкретных
аргументов.
Символические константы: #define
Если в качестве первого символа в строке программы используется символ #,
то эта строка является командной строкой препроцессора (макропроцессора).
Командная строка препроцессора заканчивается символом перевода на новую
строку. Если непосредственно перед концом строки поставить символ обратной
косой черты "", то командная строка будет продолжена на следующую строку
программы.
Директива #define, подобно всем директивам препроцессора, начинается c
символа # в самой левой позиции. Она может появиться в любом месте исходного
файла, а даваемое определение имеет силу от места появления до конца файла. Мы
активно используем эту директиву для определения символических констант в
наших примерах программ, однако она имеет более широкое применение, что мы
покажем дальше.
Замена идентификаторов
#define идентификатор строка
Пример:
#define ABC 100
Заменяет каждое вхождение идентификатора ABC в тексте программы на 100:
#undef идентификатор
Пример:
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
3
#undef ABC
Отменяет предыдущее определение для идентификатора ABC.
Пример:
/* Простые примеры директивы препроцессора */
#define TWO 2 /* можно использовать комментарии*/
#define MSG "Текст 1.
Продолжение текста 1"
/* обратная косая черта продолжает определение на следующую строку */
#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("%sn",MSG);
printf("TWO:MSGn");
}
В результате выполнения нашего примера будем иметь:
X равен 2
X равен 4
Текст 1.
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
4
Продолжение текста 1
TWO: MSG
Разберем, что произошло. Оператор
int x = TWO;
превращается в
int x = 2;
Затем оператор
PX;
превращается в
printf("X равно %d.n",x);
поскольку сделана полная замена. Теперь мы видим, что макроопределение
может представлять любую строку, даже целое выражение на языке Си. Заметим,
что это константная строка. PX напечатает только переменную, названную x.
В следующей строке выполняется следующее:
x = FOUR;
превращается
x = TWO*TWO;
превращается в
x = 2*2;
и на этом все заканчивается. Фактическое умножение имеет место не во время
работы препроцессора и не при компиляции, а всегда без исключения при работе
программы. Препроцессор не выполняет вычислений. Он только очень точно
делает предложенные подстановки. Заметим, что макроопределение может
включать другие определения. Некоторые компиляторы не поддерживают это
свойство вложения. В следующей строке
printf(FMT,x);
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
5
превращается в
printf("X равно %d.n",x)
когда FMT заменяется соответствующей строкой. Этот подход может
оказаться очень удобным, если есть длинная строка, которую мы используем
несколько раз. В следующей строке программы MSG заменяется соответствующей
строкой. Кавычки делают замещающую строку константой символьной строки.
Поскольку программа получает ее содержимое, эта строка будет запоминаться в
массиве, заканчивающемся нуль-символом. Так,
#define HAL 'X' определяет символьную константу, а
#define HAR "X" определяет символьную строку X0
Обычно препроцессор, встречая одно из макроопределений в программе,
очень точно заменяет их эквивалентной строкой замещения. Если эта строка также
содержит макроопределения, они тоже замещаются. Единственным исключением
при замене является макроопределение, находящееся внутри двойных кавычек.
Поэтому
printf("TWO: MSG");
печатает буквально TWO: MSG вместо печати следующего текста:
2: "Текст 1.
Продолжение текста 1"
Если нам нужно напечатать этот текст, можно использовать оператор
printf("%d: %sn",TWO,MSG);
потому что здесь макроопределения находятся вне кавычек.
Когда следует использовать символические константы? Вероятно, мы должны
применять их для большинства чисел. Если число является константой,
используемой в вычислениях, то символическое имя делает яснее ее смысл. Если
число - размер массива, то символическое имя упрощает изменение вашей
программы при работе с большим массивом. Если число является системным
кодом, скажем для символа EOF, то символическое представление делает
программу более переносимой. Изменяется только определение EOF.
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
6
Мнемоническое значение, легкость изменения, переносимость: все это делает
символические константы заслуживающими внимания!
Использование аргументов с #define
Во избежание ошибок при вычислении выражений параметры
макроопределения необходимо заключать в скобки.
#define идентификатор1 (идентификатор2, . . .) строка
Пример:
#define abs(A) (((A) > 0)?(A) : -(A))
Каждое вхождение выражения abs(arg) в тексте программы заменяется на
((arg) > 0) ? (arg) : -(arg),
причем параметр макроопределения А заменяется на arg.
Пример:
#define nmem (P,N)
(P) -> p_mem[N].u_long
Символ  продолжает макроопределение на вторую строчку. Это
макроопределение уменьшает сложность выражения, описывающего массив
объединений внутри структуры.
Макроопределение с аргументами очень похоже на функцию, поскольку
аргументы его заключены в скобки:
/* макроопределение с аргументами */
#define SQUARE(x) x*x
#define PR(x) printf("x равно %d.n", x)
main( )
{
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
7
int x = 4; int z;
z = SQUARE(x);
PR(z);
z = SQUARE(2);
PR(z);
PR(SQUARE(x));
PR(SQUARE(x+2));
PR(100/SQUARE(2));
PR(SQUARE(++x));
}
Всюду, где в нашей программе появляется макроопределение SQUARE(x), оно
заменяется на x*x . В отличие от наших прежних примеров, при использовании
этого макроопределения мы можем совершенно свободно применять символы,
отличные от x. В макроопределении 'x' замещается символом, использованным в
макровызове программы. Поэтому макроопределение SQUARE(2) замещается на
2*2. Таким образом, x действует как аргумент. Однако, аргумент
макроопределения не работает - точно так же, как аргумент функции. Вот
результаты выполнения программы:
z равно 16.
z равно 4.
SQUARE(x) равно 16.
SQUARE(x+2) равно 14.
100/SQUARE(2) равно 100.
SQUARE(++x) равно 30.
Первые две строки очевидны. Заметим, что даже внутри двойных кавычек в
определении PR переменная замещается соответствующим аргументом. Все
аргументы в этом определении замещаются. Рассмотрим третью строку:
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
8
PR(SQUARE(x));
Она становится следующей строкой:
printf("SQUARE(x) равно %d.n", SQUARE(x));
после первого этапа макрорасширения. Второе SQUARE(x) расширяется,
превращаясь в x*x, а первое остается без изменения, потому что теперь оно
находится внутри кавычек в операторе программы, и таким образом защищено от
дальнейшего расширения. Окончательно строка программы содержит
printf("SQUARE(x) равно %d.n",x*x);
и выводит на печать
SQUARE(x) равно x*x.
Если макроопределение включает аргумент с двойными кавычками, то
аргумент будет замещаться строкой из макровызова. Но после этого он в
дальнейшем не расширяется, даже если строка является еще одним
макроопределением. В нашем примере переменная x стала макроопределением
SQUARE(x) и осталась им. Вспомним, что x=4. Это позволяет предположить, что
SQUARE(x+2) будет равно 6*6 или 36. Но напечатанный результат говорит, что
получается число 14. Причина такого результата такова: препроцессор не делает
вычислений. Он только замещает строку. Всюду, где наше определение указывает
на x, препроцессор подставит строку x+2.
Таким образом,
x*x становится x+2*x+2
Если x равно 4, то получается
4+2*4+2=4+8+2=14
Вызов функции передает значение аргумента в функцию во время выполнения
программы. Макровызов передает строку аргументов в программу до ее
компиляции.
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
9
Макроопределение или функция?
Многие задачи можно решать, используя макроопределение с аргументами
или функцию. Что из них следует применять? На этот счет нет строгих правил, но
есть некоторые соображения.
Макроопределения должны использоваться скорее как хитрости, а не как
обычные функции. Они могут иметь нежелательные побочные эффекты.
Некоторые компиляторы ограничивают макроопределения одной строкой, и, по-
видимому, лучше соблюдать такое ограничение, даже если ваш компилятор этого
не делает.
Выбор макроопределения приводит к увеличению объема памяти, а выбор
функции - к увеличению времени работы программы. Макроопределение создает
строчный код, т.е. мы получаем оператор в программе. Если макроопределение
применить 20 раз, то в программу вставится 20 строк кода. Если мы используем
функцию 20 раз, то у нас будет только одна копия операторов функции. Однако
управление программой следует передать туда, где находится функция, а затем
вернуться в вызывающую программу, а на это потребуется больше времени, чем
при работе со строчными кодами. Так что думайте, что выбирать!
Преимущество макроопределений заключается в том, что при их
использовании нам не нужно беспокоиться о типах переменных, т.к.
макроопределения имеют дело с символьными строками, а не с фактическими
значениями. Tак наше макроопределение SQUARE(x) можно использовать
одинаково хорошо с переменными типа int или float.
Запомним!
В макроопределении нет пробелов, но они могут появиться в замещающей
строке. Препроцессор полагает, что макроопределение заканчивается на первом
пробеле, поэтому все, что стоит после пробела, остается в замещающей строке.
Используйте круглые скобки для каждого аргумента и всего определения. Это
является гарантией того, что элементы будут сгруппированы надлежащим образом
в выражении.
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
10
Для имен макрофункций следует использовать прописные буквы. Это
соглашение не распространяется так широко, как соглашение об использовании
прописных букв для макроконстант. Их применение предостережет от возможных
побочных эффектов макроопределений.
Предположим, что мы разработали несколько макрофункций по своему
усмотрению. Если мы пишем новую программу, мы не должны их переопределять.
Нужно использовать директиву #include.
Включение файла: #include
Перечень обозначений заголовочных файлов для работы с библиотеками
компиляторов утвержден стандартом языка. Ниже приведены названия этих
файлов, а также краткие сведения о тех описаниях и определениях, которые в них
включены. Большинство описаний - прототипы стандартных функций, а
определены в основном константы, например EOF, необходимые для работы с
библиотечными функциями.
assert.h - диагностика программ
ctype.h - преобразование и проверка символов
errno.h - проверка ошибок
float.h - работа с вещественными данными
limits.h - предельные значения целочисленных данных
locale.h - поддержка национальной среды
math.h - математические вычисления
setjump.h - возможности нелокальных переходов
signal.h - обработка исключительных ситуаций
stdarg.h - поддержка переменного числа параметров
stddef.h - дополнительные определения
stdio.h - средства ввода-вывода
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
11
stdlib.h - функции общего назначения (работа с памятью)
string.h - работа со строками символов
time.h - определение дат и времени
В конкретных реализациях количество и наименование заголовочных файлов
могут быть и другими. Например, в компиляторах для MS-DOS активно
используются заголовочные файлы mem.h, alloc.h, conio.h, dos.h и другие. В
компиляторах Turbo C, Borland C++ для связи с графической библиотекой
применяется заголовочный файл graphics.h.
Командная строка #include может встречаться в любом месте программы, но
обычно все включения размещаются в начале файла исходного текста.
#include <имя_файла>
Пример:
#include <math.h>
Процессор заменяет эту строку содержимым файла math.h. Угловые скобки
означают, что файл math.h будет взят из некоторого стандартного каталога (обычно
это /usr/include). Текущий каталог не просматривается:
#include "имя_файла"
Пример:
#include "ABC"
Препроцессор заменяет эту строку содержимым файла ABC. Так как имя
файла заключено в кавычки, то поиск производится в текущем каталоге (в котором
содержится основной файл исходного текста). Если в текущем каталоге данного
файла нет, то поиск производится в каталогах, определенных именем пути в опции
-l препроцессора. Если и там нет файла, то просматривается стандартный каталог.
В операционной системе UNIX угловые скобки сообщают препроцессору, что
файл следует искать в одном или нескольких стандартных системных каталогах.
Кавычки говорят ему, что сначала нужно смотреть в вашем каталоге или в каком-
то другом, если вы определяете его именем файла, а затем искать в стандартных
местах.
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
12
В конкретных реализациях количество и наименования заголовочных файлов
могут быть разными:
#include <stdio.h> ищет в системном каталоге
#include "my.h" ищет в текущем рабочем
каталоге
#include "/user/1/my.h" ищет в каталоге /user/1
В типичной микропроцессорной системе эти две формы являются
синонимами, и препроцессор ведет поиск на указанном диске.
#include "stdio.h" ищет на стандартном диске
#include <stdio.h> ищет на стандартном диске
#include "a:stdio.h" ищет на диске а
По соглашению суффикс .h используется для заголовочных файлов, т.е.
файлов с информацией, которая располагается в начале программы. Заголовочные
файлы обычно состоят из операторов препроцессора.
Некоторые файлы включены в систему, например, stdio.h, но можно создать и
свой файл.
Многие программисты разрабатывают свои стандартные заголовочные файлы,
чтобы использовать их в программах.
Условная компиляция
Командные строки препроцессора используются для условной компиляции
различных частей исходного текста в зависимости от внешних условий.
#if константное_выражение
Пример:
#if ABC + 3
Истина, если константное выражение ABC + 3 не равно нулю.
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
13
#ifdef идентификатор
Пример:
#ifdef ABC
истина, если идентификатор ABC определен ранее командой #define.
#ifndef идентификатор
Пример:
#ifndef ABC
истина, если идентификатор ABC не определен в настоящий момент.
#else
. . .
#endif
Если предшествующие проверки #if, #ifdef или #ifndef дают значение
"Истина", то строки от #else до #endif игнорируются при компиляции.
Если эти проверки дают значение "Ложь", то строчки от проверки до #else (а
при отсутствии #else - до #endif) игнорируются.
Команда #endif обозначает конец условной компиляции.
Пример:
#ifdef DEBUG
fprintf (stderr, "location: x = %dn", x);
#endif
Вспомогательные директивы
Номер строки и имя файла
#line целая_константа "имя_файла"
Пример:
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
14
#line 20 "ABC"
Препроцессор изменяет номер текущей строки и имя компилируемого файла.
Имя файла может быть опущено.
Одна из целей использования условной компиляции - сделать программу
более мобильной. Изменяя несколько ключевых определений в начале файла, мы
можем устанавливать различные значения и включать различные файлы для
разных систем.
Пример:
#define N 3/*определение константы */
void main( )
{
#line 55 "file.c"
double x[3*N];
}
Реакция на ошибки
#error последовательность лексем
Обработка директивы приводит к выдаче диагностического сообщения в виде,
определенном последовательностью лексем. Применение этой директивы
совместно с условными препроцессорными командами.
Пример:
#define NAME 15
В дальнейшем можно проверить ее значение и выдать сообщение, если у
NAME окажется другое значение:
#if (NAME !=15)
#error NAME должно быть равно 15!
Сообщение будет выглядеть так:
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
15
error <имя_файла><номер_строки >;
error directive: NAME должно быть равно 15!
Пустая директива
#
Использование этой директивы не вызывает никаких действий.
Прагмы
#pragma
Эта директива определяет действия, зависящие от конкретной реализации
компилятора. Например в некоторые компиляторы входит вариант этой директивы
для извещения компилятора о наличии в тексте программы команд на языке
Ассемблер. Возможности команды #pragma могут быть разнообазными. Стандарта
для них не существует. Если конкретный препроцессор встречает прагму, которая
ему неизвестна, он ее просто игнорирует как пустую директиву. В некоторых
реализациях включена прагма.
#pragma pack(n), где n= 1,2,4. Прагма pack позволяет влиять на упаковку
смежных элементов в структурах и объединениях (см. лекцию 14).
Соглашение может быть таким:
pack(1) - выравнивание элементов по границам байтов;
pack(2) - выравнивание элементов по границам слов;
pack(4) - выравнивание элементов по границам двойных слов;
В некоторые компиляторы включены прагмы, позволяющие изменять способ
передачи параметров функциям, порядок помещения параметров в стек и т.д.
Встроенные макроимена
Существуют встроенные (заранее определенные) макроимена, доступные
препроцессору во время обработки. Они позволяют получить следующую
информацию:
Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки
программирования»
16
_DATA_ - строка символов в формате: "месяц число год", определяющая дату
начала обработки исходного файла. Например, после препроцессорной обработки
текста программы, выполненной 29 января 2005 года, оператор
printf(_DATA_);
станет таким
printf("January 29 2005");
_LINE_ - десятичная константа - номер текущей обрабатываемой строки
файла с программой на Си. Принято, что номер первой строки исходного файла
равен 1;
_FILE_ - строка символов - имя компилируемого файла. Имя изменяется
всякий раз, когда препроцессор встречает директиву #include с указанием имени
другого файла. Когда включения файла по команде #include завершаются,
востанавливается предыдущее значение макроимени _FILE_;
_TIME_ - строка символов вида "часы:минуты:секунды", определяющая время
начала обработки препроцессором исходного файла;
_STDC_ - константа, равная 1, если компилятор работает в соответствии с
ANSI-стандартом. В противном случае значение микроимени _STDC_ не
определено. Стандарт языка Си предполагает, что наличие имени _STDC_
определяется реализацией, так как макрос _STDC_ относится к нововведениям
стандарта. В конкретных реализациях набор предопределенных имен гораздо
шире. Для получения более полных сведений о предопределенных
препроцессорных именах следует обращаться к документации по конкретному
компилятору.

More Related Content

What's hot

Теория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциямТеория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциямSergey Staroletov
 
лекции спрг 6_семестр (1)
лекции спрг 6_семестр (1)лекции спрг 6_семестр (1)
лекции спрг 6_семестр (1)djbelyakk
 
C++ осень 2013 лекция 4
C++ осень 2013 лекция 4C++ осень 2013 лекция 4
C++ осень 2013 лекция 4Technopark
 
C++ осень 2013 лекция 9
C++ осень 2013 лекция 9C++ осень 2013 лекция 9
C++ осень 2013 лекция 9Technopark
 
этапы написания программы
этапы написания программыэтапы написания программы
этапы написания программыИрина Чернова
 
Coding Standards
Coding StandardsCoding Standards
Coding Standardsroadhump
 
C++ осень 2013 лекция 5
C++ осень 2013 лекция 5C++ осень 2013 лекция 5
C++ осень 2013 лекция 5Technopark
 
Презентация. Основы Pascal
Презентация. Основы PascalПрезентация. Основы Pascal
Презентация. Основы PascalEvgen67
 
Инкапсуляция и полиморфизм в ruby
Инкапсуляция и полиморфизм в rubyИнкапсуляция и полиморфизм в ruby
Инкапсуляция и полиморфизм в rubyEvgeny Smirnov
 
C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.Igor Shkulipa
 
Типы данных
Типы данныхТипы данных
Типы данныхMonsterXX
 
C++ осень 2013 лекция 8
C++ осень 2013 лекция 8C++ осень 2013 лекция 8
C++ осень 2013 лекция 8Technopark
 
20090721 hpc exercise2
20090721 hpc exercise220090721 hpc exercise2
20090721 hpc exercise2Michael Karpov
 
паскаль язык структурного программирования
паскаль   язык структурного программированияпаскаль   язык структурного программирования
паскаль язык структурного программированияЕлена Ключева
 
О.В.Сухорослов "Паралленльные вычисления"
О.В.Сухорослов "Паралленльные вычисления"О.В.Сухорослов "Паралленльные вычисления"
О.В.Сухорослов "Паралленльные вычисления"Yandex
 
Отладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программОтладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программTatyanazaxarova
 
10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскальAnna_Malina
 
C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.Igor Shkulipa
 

What's hot (20)

Теория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциямТеория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциям
 
лекции спрг 6_семестр (1)
лекции спрг 6_семестр (1)лекции спрг 6_семестр (1)
лекции спрг 6_семестр (1)
 
C++ осень 2013 лекция 4
C++ осень 2013 лекция 4C++ осень 2013 лекция 4
C++ осень 2013 лекция 4
 
C++ осень 2013 лекция 9
C++ осень 2013 лекция 9C++ осень 2013 лекция 9
C++ осень 2013 лекция 9
 
этапы написания программы
этапы написания программыэтапы написания программы
этапы написания программы
 
Coding Standards
Coding StandardsCoding Standards
Coding Standards
 
Document
DocumentDocument
Document
 
C++ осень 2013 лекция 5
C++ осень 2013 лекция 5C++ осень 2013 лекция 5
C++ осень 2013 лекция 5
 
10 инф
10 инф10 инф
10 инф
 
Презентация. Основы Pascal
Презентация. Основы PascalПрезентация. Основы Pascal
Презентация. Основы Pascal
 
Инкапсуляция и полиморфизм в ruby
Инкапсуляция и полиморфизм в rubyИнкапсуляция и полиморфизм в ruby
Инкапсуляция и полиморфизм в ruby
 
C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.
 
Типы данных
Типы данныхТипы данных
Типы данных
 
C++ осень 2013 лекция 8
C++ осень 2013 лекция 8C++ осень 2013 лекция 8
C++ осень 2013 лекция 8
 
20090721 hpc exercise2
20090721 hpc exercise220090721 hpc exercise2
20090721 hpc exercise2
 
паскаль язык структурного программирования
паскаль   язык структурного программированияпаскаль   язык структурного программирования
паскаль язык структурного программирования
 
О.В.Сухорослов "Паралленльные вычисления"
О.В.Сухорослов "Паралленльные вычисления"О.В.Сухорослов "Паралленльные вычисления"
О.В.Сухорослов "Паралленльные вычисления"
 
Отладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программОтладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программ
 
10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль
 
C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.
 

Similar to лек13 4

C++ теория
C++ теорияC++ теория
C++ теорияtank1975
 
C++ теория
C++ теорияC++ теория
C++ теорияtank1975
 
C++ теория
C++ теорияC++ теория
C++ теорияtank1975
 
Функции в языке программирования QBasic
Функции в языке программирования QBasicФункции в языке программирования QBasic
Функции в языке программирования QBasickvlar
 
вспомогательные алгоритмы
вспомогательные алгоритмывспомогательные алгоритмы
вспомогательные алгоритмыЕлена Ключева
 
язык програмирования
язык програмированияязык програмирования
язык програмированияOlegmingalev1997
 
Запись алгоритмов на языках программирования
Запись алгоритмов на языках программированияЗапись алгоритмов на языках программирования
Запись алгоритмов на языках программированияssusere39acb
 
практика 4
практика 4практика 4
практика 4student_kai
 
Cреда программирования
Cреда программированияCреда программирования
Cреда программированияirina8682
 
4 4 pascal_urok_1
4 4 pascal_urok_14 4 pascal_urok_1
4 4 pascal_urok_1Ann Eres
 
4 4 pascal_urok_1
4 4 pascal_urok_14 4 pascal_urok_1
4 4 pascal_urok_1Ann Eres
 
Vba программы
Vba программыVba программы
Vba программыLidiKashka
 

Similar to лек13 4 (20)

лек1
лек1лек1
лек1
 
Подпрограммы
ПодпрограммыПодпрограммы
Подпрограммы
 
лек7
лек7лек7
лек7
 
лек7
лек7лек7
лек7
 
лек8
лек8лек8
лек8
 
C++ теория
C++ теорияC++ теория
C++ теория
 
C++ теория
C++ теорияC++ теория
C++ теория
 
C++ теория
C++ теорияC++ теория
C++ теория
 
лекция 4
лекция 4лекция 4
лекция 4
 
лекция 4
лекция 4лекция 4
лекция 4
 
Функции в языке программирования QBasic
Функции в языке программирования QBasicФункции в языке программирования QBasic
Функции в языке программирования QBasic
 
лр3
лр3лр3
лр3
 
вспомогательные алгоритмы
вспомогательные алгоритмывспомогательные алгоритмы
вспомогательные алгоритмы
 
язык програмирования
язык програмированияязык програмирования
язык програмирования
 
Запись алгоритмов на языках программирования
Запись алгоритмов на языках программированияЗапись алгоритмов на языках программирования
Запись алгоритмов на языках программирования
 
практика 4
практика 4практика 4
практика 4
 
Cреда программирования
Cреда программированияCреда программирования
Cреда программирования
 
4 4 pascal_urok_1
4 4 pascal_urok_14 4 pascal_urok_1
4 4 pascal_urok_1
 
4 4 pascal_urok_1
4 4 pascal_urok_14 4 pascal_urok_1
4 4 pascal_urok_1
 
Vba программы
Vba программыVba программы
Vba программы
 

More from Anastasia Snegina

птп по ппп 2013 2014
птп по ппп 2013 2014 птп по ппп 2013 2014
птп по ппп 2013 2014 Anastasia Snegina
 
прикл.прогр птп 13 14
прикл.прогр птп 13 14прикл.прогр птп 13 14
прикл.прогр птп 13 14Anastasia Snegina
 
2012 2013 пм спп провидошина
2012 2013  пм спп провидошина2012 2013  пм спп провидошина
2012 2013 пм спп провидошинаAnastasia Snegina
 
2012 2013 пм спп провидошина
2012 2013  пм спп провидошина2012 2013  пм спп провидошина
2012 2013 пм спп провидошинаAnastasia Snegina
 
рп по у пп практике в
рп по у пп практике врп по у пп практике в
рп по у пп практике вAnastasia Snegina
 
рп по пр практике в
рп по пр практике врп по пр практике в
рп по пр практике вAnastasia Snegina
 
рп по у сп практике в
рп по у сп практике врп по у сп практике в
рп по у сп практике вAnastasia Snegina
 
рп по у пп практике вт
рп по у пп практике втрп по у пп практике вт
рп по у пп практике втAnastasia Snegina
 
рп по пр практике вт
рп по пр практике втрп по пр практике вт
рп по пр практике втAnastasia Snegina
 
рп по у сп практике вт
рп по у сп практике втрп по у сп практике вт
рп по у сп практике втAnastasia Snegina
 
рп по у пп практике вт
рп по у пп практике втрп по у пп практике вт
рп по у пп практике втAnastasia Snegina
 
рп по пр практике вт
рп по пр практике втрп по пр практике вт
рп по пр практике втAnastasia Snegina
 
рп по у сп практике вт
рп по у сп практике втрп по у сп практике вт
рп по у сп практике втAnastasia Snegina
 

More from Anastasia Snegina (20)

птп по ппп 2013 2014
птп по ппп 2013 2014 птп по ппп 2013 2014
птп по ппп 2013 2014
 
прикл.прогр птп 13 14
прикл.прогр птп 13 14прикл.прогр птп 13 14
прикл.прогр птп 13 14
 
я.прогр птп
я.прогр птпя.прогр птп
я.прогр птп
 
пп кос вт
пп кос втпп кос вт
пп кос вт
 
пп кос в
пп кос впп кос в
пп кос в
 
пп кос в
пп кос впп кос в
пп кос в
 
2012 2013 пм спп провидошина
2012 2013  пм спп провидошина2012 2013  пм спп провидошина
2012 2013 пм спп провидошина
 
2012 2013 пм спп провидошина
2012 2013  пм спп провидошина2012 2013  пм спп провидошина
2012 2013 пм спп провидошина
 
пп кос вт
пп кос втпп кос вт
пп кос вт
 
рп по у пп практике в
рп по у пп практике врп по у пп практике в
рп по у пп практике в
 
рп по пр практике в
рп по пр практике врп по пр практике в
рп по пр практике в
 
рп по у сп практике в
рп по у сп практике врп по у сп практике в
рп по у сп практике в
 
рп по у пп практике вт
рп по у пп практике втрп по у пп практике вт
рп по у пп практике вт
 
рп по пр практике вт
рп по пр практике втрп по пр практике вт
рп по пр практике вт
 
рп по у сп практике вт
рп по у сп практике втрп по у сп практике вт
рп по у сп практике вт
 
рп по у пп практике вт
рп по у пп практике втрп по у пп практике вт
рп по у пп практике вт
 
рп по пр практике вт
рп по пр практике втрп по пр практике вт
рп по пр практике вт
 
рп по у сп практике вт
рп по у сп практике втрп по у сп практике вт
рп по у сп практике вт
 
лр18
лр18лр18
лр18
 
лр15
лр15лр15
лр15
 

лек13 4

  • 1. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 1 Лекция 13-4 Тема: Препроцессор языка Си. Общие сведения В интегрированную среду подготовки программ на Си или в компилятор языка как обязательный компонент входит препроцессор. Назначение препроцессора - обработка исходного текста программы до ее компиляции. Препроцессорная обработка включает несколько стадий, выполняемых последовательно. Конкретная реализация может объединять несколько стадий, но результат должен быть таким, как если бы они выполнялись в следующем порядке: Все системно-зависимые обозначения перекодируются в стандартные коды. Каждая пара из символов '' и "конец строки" вместе с пробелами между ними убираются, и тем самым следующая строка исходного текста присоединяется к строке, в которой находилась эта пара символов. В тексте распознаются директивы и лексемы препроцессора, а каждый комментарий заменяется одним символом пустого промежутка. Выполняются директивы препроцессора и производятся макроподстановки. Эскейп-последовательности в символьных константах и символьных строках заменяются на их эквиваленты. Смежные символьные строки конкатинируются, то есть соединяются в одну строку. Каждая препроцессорная лексема преобразуется в текст на языке Си. Поясним, что понимается под препроцессорными лексемами или лексемами препроцессора. К ним относятся символьные константы, имена включаемых файлов, идентификаторы, знаки операций, препроцессорные числа, знаки препинания, строковые константы и любые символы, отличные от пробела. Стадия обработки директив препроцессора. При ее выполнении возможны следующие действия:
  • 2. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 2 · замена идентификаторов заранее подготовленными последовательностями символов; · включение в программу текстов из указанных файлов; · исключение из программы отдельных частей ее текста, условная компиляция; · макроподстановка, то есть замена обозначения параметризованным текстом, формируемым препроцессором с учетом конкретных аргументов. Символические константы: #define Если в качестве первого символа в строке программы используется символ #, то эта строка является командной строкой препроцессора (макропроцессора). Командная строка препроцессора заканчивается символом перевода на новую строку. Если непосредственно перед концом строки поставить символ обратной косой черты "", то командная строка будет продолжена на следующую строку программы. Директива #define, подобно всем директивам препроцессора, начинается c символа # в самой левой позиции. Она может появиться в любом месте исходного файла, а даваемое определение имеет силу от места появления до конца файла. Мы активно используем эту директиву для определения символических констант в наших примерах программ, однако она имеет более широкое применение, что мы покажем дальше. Замена идентификаторов #define идентификатор строка Пример: #define ABC 100 Заменяет каждое вхождение идентификатора ABC в тексте программы на 100: #undef идентификатор Пример:
  • 3. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 3 #undef ABC Отменяет предыдущее определение для идентификатора ABC. Пример: /* Простые примеры директивы препроцессора */ #define TWO 2 /* можно использовать комментарии*/ #define MSG "Текст 1. Продолжение текста 1" /* обратная косая черта продолжает определение на следующую строку */ #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("%sn",MSG); printf("TWO:MSGn"); } В результате выполнения нашего примера будем иметь: X равен 2 X равен 4 Текст 1.
  • 4. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 4 Продолжение текста 1 TWO: MSG Разберем, что произошло. Оператор int x = TWO; превращается в int x = 2; Затем оператор PX; превращается в printf("X равно %d.n",x); поскольку сделана полная замена. Теперь мы видим, что макроопределение может представлять любую строку, даже целое выражение на языке Си. Заметим, что это константная строка. PX напечатает только переменную, названную x. В следующей строке выполняется следующее: x = FOUR; превращается x = TWO*TWO; превращается в x = 2*2; и на этом все заканчивается. Фактическое умножение имеет место не во время работы препроцессора и не при компиляции, а всегда без исключения при работе программы. Препроцессор не выполняет вычислений. Он только очень точно делает предложенные подстановки. Заметим, что макроопределение может включать другие определения. Некоторые компиляторы не поддерживают это свойство вложения. В следующей строке printf(FMT,x);
  • 5. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 5 превращается в printf("X равно %d.n",x) когда FMT заменяется соответствующей строкой. Этот подход может оказаться очень удобным, если есть длинная строка, которую мы используем несколько раз. В следующей строке программы MSG заменяется соответствующей строкой. Кавычки делают замещающую строку константой символьной строки. Поскольку программа получает ее содержимое, эта строка будет запоминаться в массиве, заканчивающемся нуль-символом. Так, #define HAL 'X' определяет символьную константу, а #define HAR "X" определяет символьную строку X0 Обычно препроцессор, встречая одно из макроопределений в программе, очень точно заменяет их эквивалентной строкой замещения. Если эта строка также содержит макроопределения, они тоже замещаются. Единственным исключением при замене является макроопределение, находящееся внутри двойных кавычек. Поэтому printf("TWO: MSG"); печатает буквально TWO: MSG вместо печати следующего текста: 2: "Текст 1. Продолжение текста 1" Если нам нужно напечатать этот текст, можно использовать оператор printf("%d: %sn",TWO,MSG); потому что здесь макроопределения находятся вне кавычек. Когда следует использовать символические константы? Вероятно, мы должны применять их для большинства чисел. Если число является константой, используемой в вычислениях, то символическое имя делает яснее ее смысл. Если число - размер массива, то символическое имя упрощает изменение вашей программы при работе с большим массивом. Если число является системным кодом, скажем для символа EOF, то символическое представление делает программу более переносимой. Изменяется только определение EOF.
  • 6. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 6 Мнемоническое значение, легкость изменения, переносимость: все это делает символические константы заслуживающими внимания! Использование аргументов с #define Во избежание ошибок при вычислении выражений параметры макроопределения необходимо заключать в скобки. #define идентификатор1 (идентификатор2, . . .) строка Пример: #define abs(A) (((A) > 0)?(A) : -(A)) Каждое вхождение выражения abs(arg) в тексте программы заменяется на ((arg) > 0) ? (arg) : -(arg), причем параметр макроопределения А заменяется на arg. Пример: #define nmem (P,N) (P) -> p_mem[N].u_long Символ продолжает макроопределение на вторую строчку. Это макроопределение уменьшает сложность выражения, описывающего массив объединений внутри структуры. Макроопределение с аргументами очень похоже на функцию, поскольку аргументы его заключены в скобки: /* макроопределение с аргументами */ #define SQUARE(x) x*x #define PR(x) printf("x равно %d.n", x) main( ) {
  • 7. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 7 int x = 4; int z; z = SQUARE(x); PR(z); z = SQUARE(2); PR(z); PR(SQUARE(x)); PR(SQUARE(x+2)); PR(100/SQUARE(2)); PR(SQUARE(++x)); } Всюду, где в нашей программе появляется макроопределение SQUARE(x), оно заменяется на x*x . В отличие от наших прежних примеров, при использовании этого макроопределения мы можем совершенно свободно применять символы, отличные от x. В макроопределении 'x' замещается символом, использованным в макровызове программы. Поэтому макроопределение SQUARE(2) замещается на 2*2. Таким образом, x действует как аргумент. Однако, аргумент макроопределения не работает - точно так же, как аргумент функции. Вот результаты выполнения программы: z равно 16. z равно 4. SQUARE(x) равно 16. SQUARE(x+2) равно 14. 100/SQUARE(2) равно 100. SQUARE(++x) равно 30. Первые две строки очевидны. Заметим, что даже внутри двойных кавычек в определении PR переменная замещается соответствующим аргументом. Все аргументы в этом определении замещаются. Рассмотрим третью строку:
  • 8. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 8 PR(SQUARE(x)); Она становится следующей строкой: printf("SQUARE(x) равно %d.n", SQUARE(x)); после первого этапа макрорасширения. Второе SQUARE(x) расширяется, превращаясь в x*x, а первое остается без изменения, потому что теперь оно находится внутри кавычек в операторе программы, и таким образом защищено от дальнейшего расширения. Окончательно строка программы содержит printf("SQUARE(x) равно %d.n",x*x); и выводит на печать SQUARE(x) равно x*x. Если макроопределение включает аргумент с двойными кавычками, то аргумент будет замещаться строкой из макровызова. Но после этого он в дальнейшем не расширяется, даже если строка является еще одним макроопределением. В нашем примере переменная x стала макроопределением SQUARE(x) и осталась им. Вспомним, что x=4. Это позволяет предположить, что SQUARE(x+2) будет равно 6*6 или 36. Но напечатанный результат говорит, что получается число 14. Причина такого результата такова: препроцессор не делает вычислений. Он только замещает строку. Всюду, где наше определение указывает на x, препроцессор подставит строку x+2. Таким образом, x*x становится x+2*x+2 Если x равно 4, то получается 4+2*4+2=4+8+2=14 Вызов функции передает значение аргумента в функцию во время выполнения программы. Макровызов передает строку аргументов в программу до ее компиляции.
  • 9. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 9 Макроопределение или функция? Многие задачи можно решать, используя макроопределение с аргументами или функцию. Что из них следует применять? На этот счет нет строгих правил, но есть некоторые соображения. Макроопределения должны использоваться скорее как хитрости, а не как обычные функции. Они могут иметь нежелательные побочные эффекты. Некоторые компиляторы ограничивают макроопределения одной строкой, и, по- видимому, лучше соблюдать такое ограничение, даже если ваш компилятор этого не делает. Выбор макроопределения приводит к увеличению объема памяти, а выбор функции - к увеличению времени работы программы. Макроопределение создает строчный код, т.е. мы получаем оператор в программе. Если макроопределение применить 20 раз, то в программу вставится 20 строк кода. Если мы используем функцию 20 раз, то у нас будет только одна копия операторов функции. Однако управление программой следует передать туда, где находится функция, а затем вернуться в вызывающую программу, а на это потребуется больше времени, чем при работе со строчными кодами. Так что думайте, что выбирать! Преимущество макроопределений заключается в том, что при их использовании нам не нужно беспокоиться о типах переменных, т.к. макроопределения имеют дело с символьными строками, а не с фактическими значениями. Tак наше макроопределение SQUARE(x) можно использовать одинаково хорошо с переменными типа int или float. Запомним! В макроопределении нет пробелов, но они могут появиться в замещающей строке. Препроцессор полагает, что макроопределение заканчивается на первом пробеле, поэтому все, что стоит после пробела, остается в замещающей строке. Используйте круглые скобки для каждого аргумента и всего определения. Это является гарантией того, что элементы будут сгруппированы надлежащим образом в выражении.
  • 10. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 10 Для имен макрофункций следует использовать прописные буквы. Это соглашение не распространяется так широко, как соглашение об использовании прописных букв для макроконстант. Их применение предостережет от возможных побочных эффектов макроопределений. Предположим, что мы разработали несколько макрофункций по своему усмотрению. Если мы пишем новую программу, мы не должны их переопределять. Нужно использовать директиву #include. Включение файла: #include Перечень обозначений заголовочных файлов для работы с библиотеками компиляторов утвержден стандартом языка. Ниже приведены названия этих файлов, а также краткие сведения о тех описаниях и определениях, которые в них включены. Большинство описаний - прототипы стандартных функций, а определены в основном константы, например EOF, необходимые для работы с библиотечными функциями. assert.h - диагностика программ ctype.h - преобразование и проверка символов errno.h - проверка ошибок float.h - работа с вещественными данными limits.h - предельные значения целочисленных данных locale.h - поддержка национальной среды math.h - математические вычисления setjump.h - возможности нелокальных переходов signal.h - обработка исключительных ситуаций stdarg.h - поддержка переменного числа параметров stddef.h - дополнительные определения stdio.h - средства ввода-вывода
  • 11. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 11 stdlib.h - функции общего назначения (работа с памятью) string.h - работа со строками символов time.h - определение дат и времени В конкретных реализациях количество и наименование заголовочных файлов могут быть и другими. Например, в компиляторах для MS-DOS активно используются заголовочные файлы mem.h, alloc.h, conio.h, dos.h и другие. В компиляторах Turbo C, Borland C++ для связи с графической библиотекой применяется заголовочный файл graphics.h. Командная строка #include может встречаться в любом месте программы, но обычно все включения размещаются в начале файла исходного текста. #include <имя_файла> Пример: #include <math.h> Процессор заменяет эту строку содержимым файла math.h. Угловые скобки означают, что файл math.h будет взят из некоторого стандартного каталога (обычно это /usr/include). Текущий каталог не просматривается: #include "имя_файла" Пример: #include "ABC" Препроцессор заменяет эту строку содержимым файла ABC. Так как имя файла заключено в кавычки, то поиск производится в текущем каталоге (в котором содержится основной файл исходного текста). Если в текущем каталоге данного файла нет, то поиск производится в каталогах, определенных именем пути в опции -l препроцессора. Если и там нет файла, то просматривается стандартный каталог. В операционной системе UNIX угловые скобки сообщают препроцессору, что файл следует искать в одном или нескольких стандартных системных каталогах. Кавычки говорят ему, что сначала нужно смотреть в вашем каталоге или в каком- то другом, если вы определяете его именем файла, а затем искать в стандартных местах.
  • 12. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 12 В конкретных реализациях количество и наименования заголовочных файлов могут быть разными: #include <stdio.h> ищет в системном каталоге #include "my.h" ищет в текущем рабочем каталоге #include "/user/1/my.h" ищет в каталоге /user/1 В типичной микропроцессорной системе эти две формы являются синонимами, и препроцессор ведет поиск на указанном диске. #include "stdio.h" ищет на стандартном диске #include <stdio.h> ищет на стандартном диске #include "a:stdio.h" ищет на диске а По соглашению суффикс .h используется для заголовочных файлов, т.е. файлов с информацией, которая располагается в начале программы. Заголовочные файлы обычно состоят из операторов препроцессора. Некоторые файлы включены в систему, например, stdio.h, но можно создать и свой файл. Многие программисты разрабатывают свои стандартные заголовочные файлы, чтобы использовать их в программах. Условная компиляция Командные строки препроцессора используются для условной компиляции различных частей исходного текста в зависимости от внешних условий. #if константное_выражение Пример: #if ABC + 3 Истина, если константное выражение ABC + 3 не равно нулю.
  • 13. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 13 #ifdef идентификатор Пример: #ifdef ABC истина, если идентификатор ABC определен ранее командой #define. #ifndef идентификатор Пример: #ifndef ABC истина, если идентификатор ABC не определен в настоящий момент. #else . . . #endif Если предшествующие проверки #if, #ifdef или #ifndef дают значение "Истина", то строки от #else до #endif игнорируются при компиляции. Если эти проверки дают значение "Ложь", то строчки от проверки до #else (а при отсутствии #else - до #endif) игнорируются. Команда #endif обозначает конец условной компиляции. Пример: #ifdef DEBUG fprintf (stderr, "location: x = %dn", x); #endif Вспомогательные директивы Номер строки и имя файла #line целая_константа "имя_файла" Пример:
  • 14. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 14 #line 20 "ABC" Препроцессор изменяет номер текущей строки и имя компилируемого файла. Имя файла может быть опущено. Одна из целей использования условной компиляции - сделать программу более мобильной. Изменяя несколько ключевых определений в начале файла, мы можем устанавливать различные значения и включать различные файлы для разных систем. Пример: #define N 3/*определение константы */ void main( ) { #line 55 "file.c" double x[3*N]; } Реакция на ошибки #error последовательность лексем Обработка директивы приводит к выдаче диагностического сообщения в виде, определенном последовательностью лексем. Применение этой директивы совместно с условными препроцессорными командами. Пример: #define NAME 15 В дальнейшем можно проверить ее значение и выдать сообщение, если у NAME окажется другое значение: #if (NAME !=15) #error NAME должно быть равно 15! Сообщение будет выглядеть так:
  • 15. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 15 error <имя_файла><номер_строки >; error directive: NAME должно быть равно 15! Пустая директива # Использование этой директивы не вызывает никаких действий. Прагмы #pragma Эта директива определяет действия, зависящие от конкретной реализации компилятора. Например в некоторые компиляторы входит вариант этой директивы для извещения компилятора о наличии в тексте программы команд на языке Ассемблер. Возможности команды #pragma могут быть разнообазными. Стандарта для них не существует. Если конкретный препроцессор встречает прагму, которая ему неизвестна, он ее просто игнорирует как пустую директиву. В некоторых реализациях включена прагма. #pragma pack(n), где n= 1,2,4. Прагма pack позволяет влиять на упаковку смежных элементов в структурах и объединениях (см. лекцию 14). Соглашение может быть таким: pack(1) - выравнивание элементов по границам байтов; pack(2) - выравнивание элементов по границам слов; pack(4) - выравнивание элементов по границам двойных слов; В некоторые компиляторы включены прагмы, позволяющие изменять способ передачи параметров функциям, порядок помещения параметров в стек и т.д. Встроенные макроимена Существуют встроенные (заранее определенные) макроимена, доступные препроцессору во время обработки. Они позволяют получить следующую информацию:
  • 16. Лекция №13-4 для дисциплин: «Прикладное программирование» и «Языки программирования» 16 _DATA_ - строка символов в формате: "месяц число год", определяющая дату начала обработки исходного файла. Например, после препроцессорной обработки текста программы, выполненной 29 января 2005 года, оператор printf(_DATA_); станет таким printf("January 29 2005"); _LINE_ - десятичная константа - номер текущей обрабатываемой строки файла с программой на Си. Принято, что номер первой строки исходного файла равен 1; _FILE_ - строка символов - имя компилируемого файла. Имя изменяется всякий раз, когда препроцессор встречает директиву #include с указанием имени другого файла. Когда включения файла по команде #include завершаются, востанавливается предыдущее значение макроимени _FILE_; _TIME_ - строка символов вида "часы:минуты:секунды", определяющая время начала обработки препроцессором исходного файла; _STDC_ - константа, равная 1, если компилятор работает в соответствии с ANSI-стандартом. В противном случае значение микроимени _STDC_ не определено. Стандарт языка Си предполагает, что наличие имени _STDC_ определяется реализацией, так как макрос _STDC_ относится к нововведениям стандарта. В конкретных реализациях набор предопределенных имен гораздо шире. Для получения более полных сведений о предопределенных препроцессорных именах следует обращаться к документации по конкретному компилятору.