Делает препроцессор c комментариями полосы или расширения макросов в первую очередь? [дубликат]


этот вопрос уже есть ответ здесь:

  • На каком этапе компиляции удаляются комментарии? 2 ответы

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

#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);

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

мой вопрос: что препроцессор должен делать с этим? Игнорировать замечания во-первых, или расширить макросы в первую очередь?

6   51   2009-10-02 21:26:58

6 ответов:

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

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

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

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

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

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

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

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

и стандарт C++ говорит (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;
}

что может удивить того, кто это написал.

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

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

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

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

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

#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. расширить макросы
  3. раздеться еще раз

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