Однако ключевое слово const, которое теперь поддерживается в С, обеспечивает более гибкий способ создания констант. С помощью const можно создавать глобальные и локальные константы, числовые константы, константы в форме массивов и константы в виде структур. С другой стороны, константы-макросы могут использоваться для указания размеров стандартных массивов, а также инициализирующих значений для величин const.
// допустимо
//не обязательно должно быть допустимым // допустимо
//не обязательно должно быть допустимым
Обратите внимание на комментарий “не обязательно должно быть допустимым”. В языке С предполагается, что размер массива для неавтоматических массивов задает ся целочисленным константным выражением, т.е. комбинацией целочисленных констант наподобие 5, констант из перечислений и выражений sizeof. Значения, объявленные с применением const, сюда не входят. (В этом отношении С отличается от C++, где значения const могут быть частью константных выражений.) Тем не менее, реализация компилятора может адаптировать другие формы константных выражений. В результате, к примеру, GCC 4.7.3 не примет объявление для data2, но Clang 4.6 - примет.
Лексемы
Формально тело макроса должно быть строкой лексем, а не строкой символов. Лексемы препроцессора С — это отдельные “слова” в теле определения макроса. Они отделяются друг от друга пробельными символами. Например, определение
#define FOUR 2*2
имеет одну лексему — последовательность 2*2, но определение #define SIX 2 * 3 содержит три лексемы: 2, * и 3.
Строки символов и строки лексем отличаются в том, как трактуются последовательности из множества пробелов. Рассмотрим следующее определение:
#define EIGHT 4*8
Препроцессор, который интерпретирует тело макроса как строку символов, вместо EIGHT подставляет 4 * 8. То есть дополнительные пробелы будут частью за
мены, но препроцессор, который интерпретирует тело как строку лексем, заменит EIGHT тремя лексемами, разделенными одиночными пробелами: 4*8. Другими словами, интерпретация в виде строки символов трактует пробелы как часть тела, а ин-
Препроцессор и библиотека С 667
терпретация в виде строки лексем считает пробелы разделителями между лексемами внутри тела. На практике некоторые компиляторы С рассматривают тела макросов как строки, а не как лексемы. Это различие имеет практическое значение только для более сложных случаев использования по сравнению с приведенными здесь.
Кстати, в компиляторе С принята более сложная трактовка лексем по сравнению с препроцессором. Компилятор понимает правила языка С и не обязательно требует наличия пробелов для отделения лексем друг от друга. Например, компилятор С будет интерпретировать 2*2 как три лексемы, поскольку он выясняет, что 2 является константой, а * — операцией.
Переопределение констант
Предположим, что вы определили константу LIMIT как имеющую значение 20, и затем в том же файле определили ее снова, но уже со значением 25. Такой процесс называется переопределением константы. Политика переопределения зависит от реализации компилятора. Одни реализации считают переопределение ошибкой, если только новое определение не совпадает со старым. Другие разрешают переопределение, возможно, выдавая предупреждение. В стандарте ANSI принят первый вариант, разрешающий переопределение, только если новое определение дублирует предыдущее.
Совпадение определений означает, что их тела должны иметь одни и те же лексемы в том же самом порядке. Поэтому приведенные ниже определения эквивалентны:
#define SIX 2 * 3
#define SIX 2*3
Оба определения содержат те же самые три лексемы, а избыточные пробелы не являются частью тела. Следующее определение рассматривается как отличающееся:
#define SIX 2*3
Оно содержит только одну лексему, а не три, поэтому не совпадает с предыдущими определениями. Если вы хотите переопределить макрос, применяйте директиву #undef, которую мы обсудим позже.
Если требуется переопределить некоторые константы, то для достижения цели может быть проще использовать ключевое слово const и правила области действия.
Использование аргументов в директиве #define
С помощью аргументов можно создавать функциональные макросы, которые выглядят и действуют во многом подобно обычным функциям. Макрос с аргументами очень похож на функцию, потому что аргументы заключаются в круглые скобки. Определения функциональных макросов имеют один или более аргументов в скобках, и эти аргументы затем присутствуют в выражении замены, как показано на рис. 16.2.
Читать дальше