sem09 - препроцессор_ gcc (Мини-учебник с ejudge = Чернокнижка)
Описание файла
Файл "sem09 - препроцессор_ gcc" внутри архива находится в папке "Мини-учебник с ejudge = Чернокнижка". PDF-файл из архива "Мини-учебник с ejudge = Чернокнижка", который расположен в категории "". Всё это находится в предмете "практика расчётов на пэвм" из 3 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
1Занятие №91.1 ПрепроцессорПрепроцессирование — это специальный просмотр исходного файла на языке Си, в ходекоторого выполняются специальные директивы (директивы препроцессора) и производитсямакроподстановка в тексте программы. Результатом работы препроцессора является текстовый файл, который далее попадает на вход основной стадии трансляции.Каждая директива препроцессора должна быть записана в отдельной строке файла.
Принеобходимости директива препроцессора может быть продолжена на следующую строку, если последним символом строки записать символ «обратной косой черты» \. Отличительным признаком директивы препроцессора является символ #, который должен быть первымнепробельным символом в строке.1.1.1 Директивы #define, #undefДиректива препроцессора #define позволяет задавать новые макроопределения, которые могут быть как с параметрами, так и без параметров. Определение макроса без параметров выглядит следующим образом:#define <name> <text>Здесь <name> — это имя макроса, которое должно быть идентификатором в смысле языка Си, то есть начинаться с латинской буквы или подчёркивания, за которой идут ноль илиболее латинских букв, символов подчёркивания или цифр.
Как и в языке Си, при сравненииимён учитывается регистр букв. Определяемое имя не должно быть уже определённым макросом, в противном случае выдаётся ошибка. <text> — это произвольный текст, то естьпоследовательность допустимых лексических единиц языка Си. Если в тексте встречаютсякомментарии, каждый комментарий заменяется на один символ пробела.Например,#define M_PI 3.14159265358979323846определяет макрос M_PI, а#define while /* do substitution */ doопределяет макрос while, который раскрывается в do. Поскольку макроподстановкапроисходит до синтаксического анализа программы, такое макроопределение приведёт к тому, что все ключевые слова while будут заменены на ключевые слова do.Определение макроса с параметрами выглядит следующим образом:#define <name>( <params> ) <text>Открывающая скобка не должна быть отделена пробельными символами от имени макроса.Если в списке параметров или в тексте встречаются комментарии, каждый комментарий заменяется на один символ пробела.
Параметры макроопределения — это список идентификаторов, разделённых запятыми. Параметры могут использоваться в тексте макроопределения,тогда при макроподстановке на месте имени параметра будет стоять текст соответствующегофактического параметра макроса.Например,#define swap(a,b) (a ^= b, b ^= a, a ^= b)1это возможный вариант макроса, который меняет местами две переменных какого-либоодного целого типа, а макрос#define CHECK(x) if (x < 0) { fprintf(stderr, "x < 0"); exit(1); }может использоваться при контроле допустимых значений в какой-либо функции.Текст макроопределения может использовать другие макросы.
В момент определения макросы никак не раскрываются, их полное раскрытие откладывается на момент использования макроса. Например, следующий фрагмент программы#define A B + 1#define B 2x = A + 2;присвоит переменной x значение 5.В тексте макроопределения могут использоваться две специальных операции: превращения аргумента в строку и конкатенации. Преобразование аргумента в строку записываетсякак #<param>, где <param> — это имя параметра макроса. Преобразование можно трактовать как заключение значения параметра в кавычки, а если кавычки присутствуют в текстезначения параметра, они экранируются с помощью символа \. Например, если дано макроопределение#define tostr(a) #aвызов tostr(a > b) раскроется в "a > b", а вызов tostr(puts("hello")) раскроется в "puts(\"hello\")".Операция склейки записывается как <arg1>##<arg2>, где аргументы операции —произвольные лексические единицы. Результатом склейки будет одна новая лексическаяединица.
Например, если дано макроопределение#define glue(a,b) a##bзапись glue(a,2) будет раскрыта в идентификатор a2, запись glue(d,o) будет раскрыта в ключевое слово do, а запись glue(+,+) будет раскрыта в знак операции инкремента ++.Транслятор языка Си предопределяет несколько макросов. Макрос __LINE__ всегдараскрывается в номер строки текста, в которой он используется, макрос __FILE__ раскрывается в строку, которая содержит имя просматриваемого в данный момент времени препроцессором файла, а макрос __DATE__ раскрывается в строку, которая содержит время компиляции.
Кроме того, каждый транслятор определяет дополнительные макросы, по которымможно узнать версию транслятора, операционную систему, тип процессора и пр. Например,макрос __GNUC__ раскрывается в номер версии компилятора gcc, если для компиляции используется он. Макрос __linux__ равен 1, если компиляция ведётся в системе Linux, и т.д.Директива препроцессора #undef <name> сбрасывает определение макроса с именем<name>. С этого момента макрос становится неопределённым и может использоваться, например, как обыкновенный идентификатор. Если данное имя не было определено как макрос,директива не делает ничего.1.1.2 Макроподстановки в тексте программыЕсли препроцессор находит в тексте программы идентификатор, который является именем ранее определённого макроса, препроцессор выполняет макроподстановку.
Препроцессор не выполняет макроподстановку в комментариях, символьных строках и символьныхконстантах.2Если макрос был определён как макрос без параметров, идентификатор заменяется натекст макроопределения, причём справа и слева от подставляемого текста добавляется поодному символу пробела.
Например, если макрос M_PI определён, как описано выше, текстM_PI(10) будет раскрыт в 3.14159265358979323846 (10).Если макрос был определён как макрос с параметрами, а в тексте программы сразу после идентификатора не следует символ открывающей скобки, идентификатор не изменяетсяи макроподстановки не происходит. Например, если в тексте программы используется идентификатор glue сам по себе, он не изменится в результате препроцессирования.Если в тексте программы сразу после идентификатора следует открывающая скобка,препроцессор проверяет совпадение количества параметров в макроопределении и макровызове.
В случае несовпадения выдаётся сообщение об ошибке. Далее вместо текста макровызова подставляется текст макроопределения, в котором формальные параметры замененына текст, который находится в соответствующих фактических параметрах.Препроцессор не выполняет рекурсивной макроподстановки. Если при обработкекакого-либо макровызова произвести очередное макрорасширение означало бы войти врекурсию, препроцессор оставляет макровызов без изменений. Например, пусть даны двамакроопределения#define X Y + 1#define Y X + 1В этом случае текст X раскроется в X + 1 + 1, а текст Y раскроется в Y + 1 + 1.Если в тексте-параметре макровызова используются макровызовы, макроподстановки втексте параметра выполняются до того, как текст параметра будет подставлен вместо соответствующего формального параметра. Например, если дано макроопределение#define S(a,b)текст S(a,S(b,c)) раскроется в a + b + c.1.1.3 Использование макроопределенийПоскольку препроцессирование производится до синтаксического анализа программы иможет произвольным образом менять лексическую и синтаксическую структуру программы,при использовании макросов нужно учитывать следующие детали.Лексическая вложенность.
Макроопределения не подчиняются правилам лексическойвложенности языка Си. То есть, если в тексте программы определяется макрос с именемname, ниже по тексту он может только использоваться, либо переопределяться с помощьюдирективы препроцессора #define, либо сбрасываться с помощью директивы препроцессора #undef. После директивы #undef имя становится доступным для полноценного использования в программе. Например, следующий фрагмент программы даст при компиляциисинтаксическую ошибку:#include <stdio.h>int main(void){int NULL = 0;return 0;}Имя NULL определяется в файле <stdio.h> как макрос.Приоритеты операций. Приоритеты операций в выражениях после макрорасширениймогут не совпасть с ожидаемым порядком вычисления операций. Например, пусть макрос Sопределён как3#define S(a,b) a + bПредположим, что он используется как S(1<<2,3).
Можно было бы ожидать, что результат вычисления такого выражения равен 7, но после макроподстановки получается выражение 1<<2 + 3, значение которого — 32. Поэтому в подобных случаях использованиепараметров в тексте макроопределения нужно заключать в скобки.Даже модифицированное макроопределение#define S(a,b) (a) + (b)не устраняет всех проблем. Рассмотрим выражение S(1,2)*3. Можно было бы ожидать, что его значение равно 9, однако после макроподстановки получается выражение(1)+(2)*3, значение которого равно 7. Поэтому в подобных случаях и весь текст макроопределения нужно заключать в скобки.Итак, правильное макроопределение должно выглядеть следующим образом#define S(a,b) ((a)+(b))Побочные эффекты.
Если один и тот же параметр макроса используется в его теленесколько раз, использование в качестве параметра макроса выражения с побочным эффектом может привести к неожиданным результатам. Например, пусть имеется макроопределение, которое вычисляет максимальное из двух чисел:#define max(a,b) ((a)>=(b)?(a):(b))Предположим, что значение переменной x равно 6, а значение переменной y равно 7. Тогда значение выражения max(++x,y) равно 8! В самом деле, выражение раскроется следующим образом: ((++x)>=(y)?(++x):(y)), и поскольку ++x даёт результат 7, и это жезначение будет присвоено переменной x, условие будет выполнено, поэтому выражение ++xбудет вычислено ещё один раз.Поскольку избежать повторного вычисления аргументов с побочным эффектом невозможно, макросы, которые используют свои аргументы несколько раз, должны быть задокументированы, чтобы предупредить возможные ошибки при их использовании.Границы операторов.