Сначала препроцессор C комментирует или расширяет macros?

Рассмотрим эту (ужасную, ужасную, не очень хорошую, очень плохую) структуру кода:

#define foo(x) // commented out debugging code // Misformatted to not obscure the point if (a) foo(a); bar(a); 

Я видел, что два препроцессора компиляторов генерируют разные результаты в этом коде:

 if (a) bar(a); 

а также

 if (a) ; bar(a); 

Очевидно, что это плохо для переносной базы кода.

Мой вопрос: что препроцессор должен делать с этим? Сначала Elide комментирует или сначала расшифровывает macros?

К сожалению, оригинальная спецификация ANSI C специально исключает любые функции препроцессора в разделе 4 («В этой спецификации описывается только язык C. Это не предусматривает ни библиотеку, ни препроцессор».).

Однако спецификация C99 справляется с этой эксплойтацией. Комментарии заменяются одним пространством в «фазе перевода», которое происходит до синтаксического анализа директивы Preprocessing. (Подробнее см. Раздел 6.10).

VC ++ и компилятор GNU C следуют этой парадигме – другие компиляторы могут быть несовместимыми, если они старше, но если он совместим с C99, вы должны быть в безопасности.

Как описано в этом скопированном-n-вставленном расшифровке фаз трансляции в стандарте C99, удаление комментариев (они заменены одним пробелом) происходит в фазе 3 перевода, тогда как обрабатываются директивы предварительной обработки и macros расширяются в фазе 4.

В стандарте C90 (который у меня есть только в печатном виде, поэтому нет копии-n-paste) эти две фазы происходят в одном порядке, хотя описание фаз перевода несколько отличается в некоторых деталях от стандарта C99 – факт что комментарии удаляются и заменяются одним символом пробела перед обработкой препроцессора, а macros расширены, не отличается.

Опять же, стандарт C ++ имеет эти 2 фазы в одном порядке.

Что касается того, как следует обрабатывать комментарии « // », стандарт C99 говорит об этом (6.4.9 / 2):

За исключением внутри символьной константы, строкового литерала или комментария, символы // вводят комментарий, который включает в себя все многобайтовые символы до, но не включая следующий символ новой строки.

И в стандарте C ++ говорится (2.7):

Символы // запускают комментарий, который заканчивается следующим символом новой строки.

Таким образом, ваш первый пример явно является ошибкой со стороны этого переводчика – the ; ‘после того, как foo(a) следует сохранить, когда макрос foo() будет расширен – символы комментария не должны быть частью «содержимого» макроса the foo() .

Но поскольку вы столкнулись с ошибкой переводчика, вы можете изменить определение макроса на:

 #define foo(x) /* junk */ 

для обхода ошибки.

Однако (и я отвлекаюсь от темы здесь …), так как слияние строк (обратная косая черта перед новой строкой) происходит до обработки комментариев, вы можете столкнуться с чем-то вроде этого немного неприятного кода:

 #define evil( x) printf( "hello "); // hi there, \ printf( "%s\n", x); // you! int main( int argc, char** argv) { evil( "bastard"); return 0; } 

Что может удивить любого, кто его написал.

Или даже лучше, попробуйте следующее, написанное кем-то (конечно, не я!), Которому нравятся комментарии в стиле коробки:

 int main( int argc, char** argv) { //----------------/ printf( "hello "); // Hey, what the??/ printf( "%s\n", "you"); // heck?? / //----------------/ return 0; } 

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

Согласно MSDN , комментарии заменяются одним пространством на фазе токенизации, которое происходит до фазы предварительной обработки, где macros расширяются.

Никогда не помещайте // комментарии в свои macros. Если вы должны поставить комментарии, используйте / * * /. Кроме того, у вас есть ошибка в вашем макросе:

 #define foo(x) do { } while(0) /* junk */ 

Таким образом, foo всегда безопасен в использовании. Например:

 if (some condition) foo(x); 

никогда не будет вызывать ошибку компилятора независимо от того, определено ли foo для некоторого выражения.

 #ifdef _TEST_ #define _cerr cerr #else #define _cerr / ## / cerr #endif 
  • будет работать на некоторых компиляторах (VC ++). Когда _TEST_ не определен,

    _cerr …

    будет заменена линией комментариев

    // cerr …

Я, кажется, помню, что соблюдение требует трех шагов:

  1. полоса
  2. расширять macros
  3. снова полоскать

Причина этого связана с тем, что компилятор может напрямую принимать файлы .i.

  • Почему выполняется выполнение кода Java в комментариях с некоторыми символами Unicode?
  • Давайте будем гением компьютера.