Использование макроопределений

Вы уже знаете, что, используя директиву #define, можно задавать константы. Например, если написать инструкцию #define PI 3.14, компилятор подставит значение3.14 на место всех встречающихся в программе констант PI.

Директива #define предписывает компилятору заменить имя константы на то, что следует за этим именем. Если после имени константы ввести какую-нибудь инструкцию, компилятор тоже произведет подстановку. Например, в следующей инструкции мы подставляем на место константы ENTER функцию printf():

#define ENTER printf("Пожалуйста, введите число: ")

Теперь, при необходимости отобразить сообщение, записанное в аргументе функции printf(), достаточно в соответствующем месте программы использовать инструкцию ENTER:

#define ENTER printf("Пожалуйста, введитечисло: ")main() { int age, size; ENTER; scanf("%d", &age); ENTER; scanf("%d", &size); }


Рис. 9. Использование макроопределения

При выполнении программы сообщение «Пожалуйста, введите число:» будет появляться на экране точно так же, как если бы в main() была полностью написана инструкция, содержащая функцию printf() (рис.9).

Директивы, подобные той, которую мы только что рассмотрели, называются макроопределениями или макросами*. Они являются очень мощным средством, позволяющим избежать необходимости вручную вводить одну и ту же инструкцию несколько раз в одной программе. Например, макрос ENTER можно использовать всякий раз, когда нужно подсказать пользователю, что он должен ввести данные. Еще более мощным программным средством макроопределения делает то обстоятельство, что им можно передавать аргументы, так же, как функциям. В приведенной ниже программе, например, макрос CONVERT используется для перевода значения температуры из шкалы Фаренгейта в шкалу Цельсия:

#define ENTER printf("Пожалуйста, введите значение температуры: ")#define CONVERT(temp) (5.0 / 9.0) * (temp - 32)main() { float climate; ENTER; scanf("%f", &climate); printf("это соответствует %f по шкале Цельсия\n", CONVERT(climate)); }


Рис. 10. Определение выражения как макроса

Во второй директиве #define определяется макрос CONVERT, который требует передачи ему одного значения. Аргумент, принимаемый макросом CONVERT, подставляется в выражение (5.0 / 9.0) * (temp - 32) на место слова temp. Вызов макроса осуществляется способом, аналогичным вызову функции, с использованием в качестве аргумента значения, которое мы хотим преобразовать (рис.10). Если в ответ на запрос введено значение212, происходит вызов CONVERT в функции printf() и расчет значения выражения (5.0 / 9.0) * (212 - 32).

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

Проектирование программы

В целом, не существует никаких строгих правил, указывающих, когда следует использовать функции, а когда можно поместить все инструкции в функцию main(). Со временем, когда вы приобретете опыт, решение будет возникать само собой. Если вы пишете короткую программу, где вводится одно-два числа, выполняются математические операции и результат выводится на экран, то можно ограничиться функцией main(). В самом деле, было бы глупо делить программу, текст которой включает пять или десять строк, на функции без особой на то необходимости.

С другой стороны, если вы написали длинную программу, поместив все ее инструкции в main(), и оказалось, что где-то закралась ошибка, обнаружить ее будет весьма непросто. Если же программа разделена на функции, вы можете начать диагностику с вопроса: «Какая функция с наибольшей вероятностью является источником проблемы?» и искать ошибку в отдельных частях программы, начав с наиболее вероятного места ее нахождения.