Удаляет ли препроцессор C комментарии или сначала разворачивает макросы?[дубликат]
-
19-09-2019 - |
Вопрос
На этот вопрос уже есть ответ здесь:
Рассмотрим эту (ужасную, ужасный, никуда не годный, очень плохой) структуру кода:
#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);
Очевидно, что это плохо для переносимой базы кода.
Мой вопрос:Что препроцессор должен с этим делать?Сначала удалите комментарии или сначала разверните макросы?
Решение
К сожалению, оригинал Спецификация ANSI C в частности, исключаются любые функции препроцессора в разделе 4 ("Эта спецификация описывает только язык C.Это не предусматривает ни библиотеки, ни препроцессора".).
В Спецификация 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;
}
Что могло бы удивить того, кто это написал.
Или, что еще лучше, попробуйте следующее, написанное кем-то (конечно, не мной!) кому нравятся комментарии в стиле box:
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_
не определено,_серр ...
будет заменена строкой комментария
// серр ...
Кажется, я припоминаю, что соблюдение требований требует трех шагов:
- Стриптиз
- развернуть макросы
- раздевайся снова
Причина этого связана с тем, что компилятор может принимать файлы .i напрямую .