Подтвердить что ты не робот

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

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

#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, или сначала раскройте макросы?

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

В отношении того, как следует обрабатывать комментарии "//", стандарт C99 говорит об этом (6.4.9/2):

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

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

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

Итак, ваш первый пример, очевидно, является ошибкой со стороны этого переводчика - символом ; "после того, как 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;
}

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

if (some condition)
    foo(x);

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

Ответ 5

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

    _cerr...

    будет заменен на строку комментария

    //cerr...

Ответ 6

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

  1. полосы
  2. expand макросы
  3. strip снова

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