Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2014_2015 / LECT14_2015.pptx
Скачиваний:
10
Добавлен:
27.12.2015
Размер:
178.88 Кб
Скачать

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

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

ключевые слова C++ (C) как идентификаторы макросов:

#define int long /* допустимо, но может привести к катастрофическим последствиям */

#define INT long /* допустимо и может быть полезно */

Следующие предопределенные глобальные идентификаторы не могут появляться непосредственно следом за директивами #define или #undef:

__STDC__ __DATE__ __FILE__ __TIME__ __LINE__ Отметим наличие в этих именах 2х двойных символов подчеркивания.

#define идентификатор_макроса(<список- аргументов>) последовательность-лексем Любая запятая в круглых скобках внутри аргумента

рассматривается как часть аргумента, а не как разделитель аргументов.

!!!! между идентификатором-макроса и левой круглой скобкой списка-аргументов не может находиться ни одного пробельного символа.!!!! Опциональный список- аргументов – последователь-ность идентификаторов, разделенных запятыми, как в списке аргументов функции С. Каждый разделенный запятой идентификатор играет

Вызов таких макросов: идентификатор-макроса<пробельный- символ>(<список-фактических-аргументов>)

в последующем исходном коде. Синтаксис вызова аналогичен синтаксису вызова ф-ий; многие стандартные библиотечные "функции" С++ (С) реализованы в виде макросов.

Возможные различия с вызовами ф-ий, которые могут привести к случайным ошибкам:

опциональный список-фактических-аргументов должен содержать то же число разделяемых запятой лексем, известных как фактические аргументы, что содержится в списке-формальных-аргументов в строке с #define: каждому формальному аргументу должен соответствовать один фактический аргумент. Если число аргументах в двух указанных списков различно, то ошибка.

Вызов макроса приводит к двум типам замены:

1)идентификатор макроса и заключенные в круглые скобки аргументы заменяются последовательностью лексем.

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

списка-фактических-аргументов. #define CUBE(x) ((x)*(x)*(x))

...

int n,y

n = CUBE(y):

дает в результате следующую замену: n = ((y)*(y)*(y));

Аналогичным образом, последняя строка в #define SUM ((a) + (b))

... избыток круглых скобок??? int i,j,sum;

sum = SUM(i,j);

!!! На местах все скобки!!!: n = CUBE(y+1);

Без внутренней пары круглых скобок расширение даст запись: n=y+1*y+1*y+1, т.е. анализе равно:

n = y + (1*y) + (1*y) + 1;

А должно в куб возводиться (y+1) !

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

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

1. Вложенные круглые скобки и запятые: Список-фактических-аргументов может содержать вложенные круглые скобки, при условии соответствия числа открывающих числу закрывающих скобок;, запятые, заключенные во внутренние круглые скобки или кавычки,

не рассматриваются в качестве разделителей

Пример:

#define ERRMSG(x, str) showerr("Error",x,str) #define SUM(x,y) ((x) + (y))

...

ERRMSG(2, "Press Enter, then Esc");

/* расширится в: showerr("Error",2,"Press Enter, then Esc"); */

return SUM(f(i,j), g(k.l));

/* расширится в: return ((f(i,j)) + (g(k,l))); */

2. Склеивание лексем ##: можно выполнить склеивание (слияние) двух лексем, разделив их символами ## (и плюс опциональными пробельными символами с каждой стороны). Препроцессор удаляет пробельные символы и ##, объединяя две отдельные лексемы в одну новую лексему. Это средство можно использовать для конструирования идентификаторов; например: #define VAR(i,j) (i##j)

и затем вызвав VAR(x,6), можно получить расширение (x6).

3.Преобразование к строкам при помощи #: символ # можно поместить перед формальным аргументом макроса с тем, чтобы после подстановки фактический аргумент был преобразован в строку:

#define TRACE(flag) printf("#flag =%d\n",flag) фрагмент кода

int highval = 1024; TRACE(highval); станет равным int highval = 1024;

printf(""highval" = %d\n", highval);

что в свою очередь будет рассматриваться как int highval = 1024;

printf("highval=%d\n", highval);

4.Символ обратной наклонной черты для продолжения строки: длинная последовательность лексем может продлить строку при помощи обратной наклонной черты (\). Символы обратной наклонной

черты и новой строки оба вырезаются, и образуется

#define WARN "this is one very\ long string message"

...

puts(WARN);

/* this is one very long string message */

5. Побочные эффекты и прочие глюки: схожесть между вызовами макросов и функциями иногда скрывает различия между ними:

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

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

раза.

Сравните CUBE и cube:

int cube(int x) {return x*x*x;} #define CUBE(x) ((x)*(x)*(x))

...

int b =0, a = 3; b = cube(a++); a = 3;

b = CUBE(a++);

/* расширяется в: ((a++)*(a++)*(a++)); */

Итоговое значение b зависит от того, что компилятор делает с расширенным выражением –приоритеты операций

/*_null.h Definition of NULL.*/-файл _null.h в BC 3.11 #ifndef NULL

#

if defined(__TINY__) || defined(__SMALL__) ||

defined(__MEDIUM__)

#

define NULL 0

#

else

Условная компиляция - исходный файл можно компили- ровать не целиком, а частями, используя директивы:

1) #if константное_выражение Пример: #if ABC + 3

Истина, если константное выражение ABC+3 не равно нулю.

2) #ifdef идентификатор Пример: #ifdef ABC

Истина,если идентификатор ABC определен ранее командой #define.

3) #ifndef идентификатор Пример: #ifndef ABC

Истина, если идентификатор ABC не определен в настоящее время.

4) #else

. . .

#endif

Если предшествующие проверки #if, #ifdef или #ifndef дают значение "Истина", то строки от #else до #endif игнорируются при компиляции.

Если эти проверки дают значение "Ложь", то строчки

Соседние файлы в папке 2014_2015