В выражениях #if и #elif что определяется префиксом для имен и что с идентификаторами, включая ключевые слова C, заменяемые на 0?

Я попытался обдумать это, но сдался. Не могли бы вы объяснить следующее, взятое из книги C Майка Банахана (Раздел 7.3. 7 Условная компиляция). Несмотря на многочисленные попытки, я не могу понять часть после "Последовательность токенов, которая составляет...". Не могли бы вы изложить простыми словами

1) Что такое «определенный» префикс, и

2) Что с именами и даже ключевыми словами C, сведенными к нулю (не забывая о запутанной ссылке на sizeof)?

Объяснение с небольшим фрагментом кода будет очень полезно. Спасибо.

Конструкции #if и #elif принимают в качестве аргументов одно целочисленное константное выражение. Целочисленные константные выражения препроцессора такие же, как и другие целочисленные константные выражения, за исключением того, что они не должны содержать операторов приведения. Последовательность токенов, составляющая константное выражение, заменяется макросом, за исключением того, что имена с префиксом define не расширяются. В этом контексте выражение определено NAME или определено ( NAME ) оценивается как 1, если NAME определено в данный момент, и 0, если не определено. Любые другие идентификаторы в выражении, включая те, которые являются ключевыми словами C, заменяются значением 0. Затем вычисляется выражение. Замена даже ключевых слов означает, что sizeof нельзя использовать в этих выражениях для получения ожидаемого результата.


person Thokchom    schedule 28.09.2014    source источник
comment
#if defined NAME – gcc.gnu.org/onlinedocs/cpp/Defined.html   -  person Karoly Horvath    schedule 28.09.2014
comment
@KarolyHorvath Ну, теперь я понял ... но как насчет того, чтобы имена, включая ключевые слова C, были заменены на 0? Можете ли вы дать небольшой пример кода? И что именно означает последняя строка о sizeof?   -  person Thokchom    schedule 28.09.2014


Ответы (2)


Это относится к включению или отключению условной компиляции. В нем обсуждается как использование целочисленных выражений, так и макросов препроцессора.

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

#define DEBUGGING 1

#if DEBUGGING
    printf( "I'm debugging\n" );
#endif

void function( void ) {
   if ( DEBUGGING ) {
       printf( "I'm debugging\n" );
   }
}

Этот код будет работать, т. е. вы получите два печатных оператора вывода, которые говорят: «Я отлаживаю».

Причина в том, что в первом блоке (#if DEBUGGING) макрос DEBUGGING преобразуется в 1 (значение, которое он должен представлять), а затем оценивается как целочисленное выражение. Другими словами, препроцессор сводит код к следующему:

#if 1
    printf( "I'm debugging\n" );
#endif

void function( void ) {
   if ( 1 ) {
       printf( "I'm debugging\n" );
   }
}

Но теперь рассмотрим случай, когда макрос #DEBUGGING вообще не определен.

В этом случае препроцессор сократит код до такого:

//#define DEBUGGING 1   <-- commented out

#if 0  // <--- DEBUGGING is not a macro-word here, so it is converted to 0
    printf( "I'm debugging\n" );
#endif

void function( void ) {
   if ( DEBUGGING ) { // <---- look what happened here! *Not* in "#if", so NOT preprocessed to zero!
       printf( "I'm debugging\n" );
   }
}

Из-за того, как определен язык C, тест #if уменьшит любое слово, которое не распознается как макрос (например, int, sizeof, myVariableName, SOME_OTHER_UNDEFINED_THING), до 0, и ошибка не будет выдана. Первый оператор printf просто не будет скомпилирован.

Однако второй оператор, использующий то, что было макросом в предыдущей версии кода, НЕ получит замену, поскольку макрос DEBUGGING больше не определен. В этом случае после запуска препроцессора компилятор будет жаловаться на имя, которое он не распознает, поскольку он буквально увидит слово DEBUGGING и не будет знать, что с ним делать (это не имя переменной или ключевое слово и т. ).

Это первое, о чем говорит процитированный вами абзац.

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

#define VERSION 2

#if VERSION > 1
    printf( "this code only runs on v2+\n" );
#endif

Препроцессор сначала заменяет слово VERSION тем, что оно представляет:

#if 2 > 1
...

затем препроцессор оценивает строку как целочисленное выражение. Поскольку 2 больше 1 (истина), выражение/строка сводится к:

#if 1
    printf( "this code only runs on v2+\n" );
#endif

Что касается sizeof (и любого другого ключевого слова или слова, не являющегося макросом), в операторах #if все имена, которые не распознает препроцессор, преобразуются в ноль. Это сделано для того, чтобы он мог обрабатывать условную компиляцию макросов, которых нет, как показано выше, когда я закомментировал DEBUGGING.

Итак, этот код, который выглядит как то, что вы, возможно, захотите сделать:

#if sizeof(int) > 2
    printf( "This machine has 4+ byte integers\n" );
#endif

Сократится до этого:

#if 0(0) > 2  // sizeof and int are non-macro-words so they get replaced with 0

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

Если вы думаете об этом, отделив препроцессор C от компилятора C, это имеет смысл. Препроцессор не знает об обычных ключевых словах C, это не его работа. Он просто анализирует интересующие его токены (#if и т. д.), а затем передает обработанный C-файл компилятору.

person par    schedule 28.09.2014
comment
Развернутый ответ!! Именно так, как мне было нужно. Спасибо. +1. - person Thokchom; 28.09.2014

«определенный» здесь относится к директиве препроцессора #define.

Раздел исходного кода может включать #define следующим образом:

#define UNITTEST

Тогда позже вы можете написать

#ifdef UNITTEST
  runtest();
#endif

Есть по крайней мере два способа определить символ для препроцессора. Первый — включить #define в файл, как указано выше. Второй — включить -D<symbol> директив из gcc командной строки, как в -DUNITTEST. В этом случае символ UNITTEST будет определяться на протяжении всей компиляции.

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

#define ABBV 
...
#undef ABBV

При запуске модульных тестов укажите системе сборки (make, SCons) применить директиву -D и единообразно закодировать исходные файлы для ветвления на этом символе.

Обратите внимание, что #define может определять символы в виде текста, который вы цитируете (UNITTEST преобразуется в 1, если он определен), или он может определять маркировку с аргументом. Канонический макрос

#define SQR(x) ((x) * (x))
int a(1);
int b(SQR(a));

Хорошей практикой остается #undef макрос после его использования или помещение "глобальных" макросов в отдельный файл для облегчения идентификации. Итак, учитывая, что я написал SQR выше, я бы последовал за

#undef SQR

Ссылаясь на ваш конкретный вопрос об условной компиляции, я использую директивы -D (часто много) в командной строке компилятора при создании тестов, разработки или производства. Затем я проверяю эти символы в исходном коде. И для #defines в исходном коде я определяю либо локальные макросы (например, макрос SQR) и отменяю их, либо записываю глобальные макросы в один файл core_macros.h.

person JayInNyc    schedule 28.09.2014
comment
Когда DEBUG не определено, препроцессор сгенерирует if (DEBUG) не if (0). Вот почему вы получаете ошибку в тесте if, а не молча. - person par; 28.09.2014
comment
как насчет того, чтобы поцарапать эту часть моего ответа. На самом деле я никогда так не делаю, так что мне следовало оставить это как есть. - person JayInNyc; 28.09.2014
comment
@JayInNyc +1. Спасибо, что подтолкнули меня к моему не очень крутому вопросу :-)! - person Thokchom; 28.09.2014
comment
@Thokchom рад, что это помогло. @par и я дали разные взгляды на ответ. @par точно ответил на ваш вопрос, мой больше касается лучших практик при использовании #defines. Ваше здоровье - person JayInNyc; 28.09.2014