Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп
Шрифт:
Интервал:
Закладка:
FILE* fo = fopen(fn2, "w"); /* открываем файл fn для записи */
if (fi == 0) error("невозможно открыть файл для ввода");
if (fo == 0) error("невозможно открыть файл для вывода");
/* чтение из файла с помощью функций ввода из библиотеки stdio,
например, getc() */
/* запись в файл с помощью функций вывода из библиотеки stdio,
например, fprintf() */
fclose(fo);
fclose(fi);
}
Учтите: в языке С нет исключений, потому вы не можете узнать, что при обнаружении ошибок файлы были закрыты.
27.7. Константы и макросы
В языке С константы не являются статическими.
const int max = 30;
const int x; /* неинициализированная константа: OK в C
(ошибка в C++) */
void f(int v)
{
int a1[max]; /* ошибка: граница массива не является константой
(OK в языке C++) */
/* (слово max не допускается в константном
выражении!) */
int a2[x]; /* ошибка: граница массива не является константой */
switch (v) {
case 1:
/* ... */
break;
case max: /* ошибка: метка раздела case не является
константой (OK в языке C++) */
/* ... */
break;
}
}
По техническим причинам в языке С (но не в языке C++) неявно допускается, чтобы константы появлялись из других модулей компиляции.
/* файл x.c: */
const int x; /* инициализирована в другом месте */
/* файл xx.c: */
const int x = 7; /* настоящее определение */
В языке С++ в разных файлах могут существовать два разных объекта с одним и тем же именем x. Вместо использования ключевого слова const для представления символьных констант программисты на языке С обычно используют макросы. Рассмотрим пример.
#define MAX 30
void f(int v)
{
int a1[MAX]; /* OK */
switch (v) {
case 1:
/* ... */
break;
case MAX: /* OK */
/* ... */
break;
}
}
Имя макроса MAX заменяется символами 30, представляющими собой значение этого макроса; иначе говоря, количество элементов массива a1 равно 30, а меткой второго раздела case является число 30. По общепринятому соглашению имя макроса MAX состоит только из прописных букв. Это позволяет минимизировать ошибки, вызываемые макросами.
27.8. Макросы
Берегитесь макросов: в языке С нет по-настоящему эффективных способов избежать макросов, но их использование имеет серьезные побочные эффекты, поскольку они не подчиняются обычным правилам разрешения области видимости и типов, принятым в языках С и С++. Макросы — это вид текстуальной подстановки. См. также раздел А.17.2.
Как защититься от потенциальных проблем, связанных с макросами, не отказываясь от них навсегда (и не прибегая к альтернативам, предусмотренным в языке С++?
• Присваивайте всем макросам имена, состоящие только из прописных букв: ALL_CAPS.
• Не присваивайте имена, состоящие только из прописных букв, объектам, которые не являются макросами.
• Никогда не давайте макросам короткие или “изящные” имена, такие как max или min.
• Надейтесь, что остальные программисты следуют этим простым и общеизвестным правилам.
В основном макросы применяются в следующих случаях:
• определение “констант”;
• определение конструкций, напоминающих функции;
• улучшение синтаксиса;
• управление условной компиляцией.
Кроме того, существует большое количество менее известных ситуаций, в которых могут использоваться макросы.
Мы считаем, что макросы используются слишком часто, но в программах на языке С у них нет разумных и полноценных альтернатив. Их даже трудно избежать в программах на языке С++ (особенно, если вам необходимо написать программу, которая должна подходить для очень старых компиляторов или выполняться на платформах с необычными ограничениями).
Мы приносим извинения читателям, считающим, что приемы, которые будут описаны ниже, являются “грязными трюками”, и полагают, что о них лучше не говорить в приличном обществе. Однако мы думаем, что программирование должно учитывать реалии и что эти (очень простые) примеры использования и неправильного использования макросов сэкономят часы страданий для новичков. Незнание макросов не приносит счастья.
27.8.1. Макросы, похожие на функции
Рассмотрим типичный макрос, напоминающий функцию.
#define MAX(x, y) ((x)>=(y)?(x):(y))
Мы используем прописные буквы в имени MAX, чтобы отличить его от многих функций с именем max (в разных программах). Очевидно, что этот макрос сильно отличается от функции: у него нет типов аргументов, нет тела, нет инструкции return и так далее, и вообще, зачем здесь так много скобок? Проанализируем следующий код:
int aa = MAX(1,2);
double dd = MAX(aa++,2);
char cc = MAX(dd,aa)+2;
Он разворачивается в такой фрагмент программы:
int aa = ((1)>=( 2)?(1):(2));
double dd = ((aa++)>=(2)?( aa++):(2));
char cc = ((dd)>=(aa)?(dd):(aa))+2;
Если бы всех этих скобок не было, то последняя строка выглядела бы следующим образом.
char cc = dd>=aa?dd:aa+2;
Иначе говоря, переменная cc могла бы легко получить другое значение, которого вы не ожидали, исходя из определения макроса. Определяя макрос, не забывайте заключить в скобки каждый аргумент, входящий в выражение.
С другой стороны, не всегда скобки могут спасти нас от второго варианта развертывания. Параметру макроса x было присвоено значение aa++, а поскольку переменная x в макросе MAX используется дважды, переменная a может инкрементироваться также дважды. Не передавайте макросу аргументы, имеющие побочные эффекты.
Какой-то “гений” определил макрос следующим образом и поместил его в широко используемый заголовочный файл. К сожалению, он также назвал его max, а не MAX, поэтому когда в стандартном заголовке языка C++ объявляется функция
template<class T> inline T max(T a, T b) { return a<b?b:a; }
имя max разворачивается с аргументами T a и T b, и компилятор видит строку
template<class T> inline T ((T a)>=(T