Читать интересную книгу Язык Си - руководство для начинающих - M. УЭИТ

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 62 63 64 65 66 67 68 69 70 ... 98

#define BIG 3

#define HUGE 5

#undef BIG              /* BIG теперь не определен */

#define HUGE 10        /* HUGE переопределен как 10 */

#undef HUGE           /* HUGE снова равен 5*/

#undef HUGE           /* HUGE теперь не определен */

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

     Или, предположим, что вы работаете с большой системой программ. Вы хотите задать макроопределение, но не уверены, использует ли ваше определение другие определения откуда-нибудь из другого места системы. В этом случае просто отмените ваше макроопределение в том месте, где оно вам больше не нужно, а оригинал этого макроопределения, если он есть, будет по-прежнему оставаться в силе для остальной части системы.

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

#ifdef MAVIS 

#include " horse.h" /* выполнится, если MAVIS определен */

#define STABLES 5

#else

#include "cow.h" /*выполнится, если MAVIS не определен */

#define STABLES 15

#endif

     Директива #ifdef сообщает, что если последующий идентификатор (MAVIS) определяется препроцессором, то выполняются все последующие директивы вплоть до первого появления #else или #endif. Когда в программе есть #else, то программа от #else до #endif будет выполняться, если идентификатор не определен.

     Такая структура очень напоминает конструкцию if-else языка Си. Основная разница заключается в том, что препроцессор не распознает фигурные скобки {}, отмечающие блок, потому что он использует директивы #else (если есть) и #endif (которая должна быть) для пометки блоков директив.

     Эти условные конструкции могут быть вложенными.

     Директивы #ifdef и #if можно использовать с #else и #endif таким же образом. Директива #ifndef опрашивает, является ли последующий идентификатор неопределенным; эта директива противоположна #ifdef. Директива #if больше похожа на обычный оператор if языка Си. За ней следует константное выражение, которое считается истинным, если оно не равно нулю:

#if SYS == "IBM"

#include "ibm.h"

#endif

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

     Эти краткие примеры иллюстрируют удивительную способность языка Си изощренно и строго управлять программами.

ЧТО ВЫ ДОЛЖНЫ БЫЛИ УЗНАТЬ В ЭТОЙ ГЛАВЕ

Как определять символьные константы директивой #define: #define FINGERS 10

Как включать другие файлы: #include "albanian.h"

Как определить макрофункцию: #define NEG(X) (-(X))

Когда использовать символические константы: часто.

Когда использовать макрофункции: иногда.

Опасность применения макрофункций: побочные эффекты.

ВОПРОСЫ И ОТВЕТЫ 

Вопросы

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

a. #define FPM 5280 /* футов в миле */

dist = FPM * miles;

б. #define FEET 4

#define POD FEET + FEET

plort = FEET * POD;

в. #define SIX = 6;

nex = SIX;

г. #define NEW(X) X + 5

у = NEW(y);

berg = NEW(berg) * lob;

est = NEW(berg) / NEW(y);

nilp = lob * NEW(-berg);

2. Подправьте определение в вопросе 1.г, чтобы сделать его более надежным.

3. Определите макрофункцию, которая возвращает минимальное из двух значений.

4. Задайте макроопределение, в котором есть функция whitesp(с) считающая в программе пустые символы.

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

Ответы

1.

а. dist = 5280 * miles; правильно.

б. plot = 4 * 4 + 4; правильно, но если пользователь на самом деле хотел иметь 4 * (4 + 4), то следовало применять директиву #define POD (FEET + FEET).

в. nex = = 6; неправильно; очевидно, пользователь забыл, что он пишет для препроцессора, а не на языке Си.

г. у - у + 5; правильно.

berg = berg + 5 * lob; правильно, но, вероятно, это нежелательный результат.

est = berg + 5/у + 5; то же самое.

nilp = lob * -berg + 5; то же самое.

2. #define NEW(X) ((X) + 5)

3. #deline MIN(X,Y) ((X) < (Y) ? (X) : (Y))

4. #define WHITESP(C) ((С) == ' ' || (С) == 'n' || (С)) == 't')

5. #define PR2(X,Y) printf(" X равно %d и Y равно %d.n", X, Y)

Так как в этом макроопределении Х и Y никогда не используются никакими другими операциями (такими, как умножение), мы не должны ничего заключать в скобки.

УПРАЖНЕНИЕ

1. Создайте заголовочный файл определений препроцессора, которые вы хотите применять.

12. Массивы и указатели

МАССИВЫ. МНОГОМЕРНЫЕ МАССИВЫ. ИНИЦИАЛИЗАЦИЯ МАССИВОВ. УКАЗАТЕЛИ И ОПЕРАЦИИ НАД УКАЗАТЕЛЯМИ. СВЯЗЬ МЕЖДУ МАССИВОМ И УКАЗАТЕЛЕМ. ОПЕРАЦИИ  & * (унарные)

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

МАССИВЫ 

    Вы уже знаете, что массив представляет собой группу элементов одного типа. Когда нам требуется для работы массив, мы сообщаем об этом компилятору при помощи операторов описания. Для создания массива компилятору необходимо знать тип данных и требуемый класс памяти, т. е. то же самое, что и для простой переменной (называемой "скалярной"). Кроме того, должно быть известно, сколько элементов имеет массив. Массивы могут иметь те же типы данных и классы памяти, что и простые переменные, и к ним применим тот же принцип умолчания. Рассмотрим примеры, различных описаний массивов:

/* несколько описаний массивов */

int temp[365]; /* внешний массив из 365 целых чисел */

main( )

{

float rain[365];  /* автоматический массив из 365 чисел типа

float */

static char code[12];  /* статический массив из 12 символов */

extern temp[ ]; /* внешний массив; размер указан выше */

}

Как уже упоминалось, квадратные скобки ([ ]) говорят о том, что temp и все остальные идентификаторы являются именами массивов, а число, заключенное в скобки, указывает количество элементов массива. Отдельный элемент массива определяется при помощи его номера, называемого также индексом. Нумерация элементов начинается с нуля, поэтому temp[0] является первым, а temp[364] последним 365-элементом массива temp. Но все это вам уже должно быть известно, поэтому изучим что-нибудь новое.

Инициализация массивов и классы памяти

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

     Мы знаем, что скалярные переменные можно инициализировать в описании типа при помощи таких выражений, как, например:

int fix = 1;

float flах = PI*2;

при этом предполагается, что PI - ранее введенное макроопределение. Можем ли мы делать что-либо подобное с массивом? Ответ не однозначен: и да, и нет.

     Внешние и статические массивы можно инициализировать.

     Автоматические и регистровые массивы инициализировать нельзя.

1 ... 62 63 64 65 66 67 68 69 70 ... 98
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Язык Си - руководство для начинающих - M. УЭИТ.
Книги, аналогичгные Язык Си - руководство для начинающих - M. УЭИТ

Оставить комментарий