Сначала препроцессор 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);
а также
- Удалить комментарии к исходному файлу с помощью IntelliJ?
- Комментарии в Markdown
- Автоматически добавлять комментарии к файлам PDF с регулярными выражениями
- Удалить комментарии из кода C / C ++
- Каков наилучший комментарий в исходном коде, который вы когда-либо встречали?
if (a) ; bar(a);
Очевидно, что это плохо для переносной базы кода.
Мой вопрос: что препроцессор должен делать с этим? Сначала Elide комментирует или сначала расшифровывает macros?
- Как процитировать «* /» в JavaDocs
- Как прокомментировать блок тегов в XML?
- / ** и / * в Java Комментарии
- Как вам нравятся ваши комментарии?
- Как работают Data.MemoCombinators?
- Многострочные методы обхода комментариев?
- Могут ли комментарии использоваться в JSON?
- Есть ли ярлык, чтобы сделать комментарий блока в Xcode?
К сожалению, оригинальная спецификация 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 …
Я, кажется, помню, что соблюдение требует трех шагов:
- полоса
- расширять macros
- снова полоскать
Причина этого связана с тем, что компилятор может напрямую принимать файлы .i.