В программистской практике регулярно случается ситуация, когда на время разработки и отладки требуется включать какой-то код и выключать другой. Это несложно делать специальными конструкциями типа
#if true ... #else ... #endif
, меняя true
на false
, или прибегая к более изощренным условиям. В языках, поддерживающих комментарий типа
/* ... */
c помощью слегка нестандартной конструкции /**/ ... /*/ ... /*/ ... /**/
можно создавать сколько угодно чередующихся участков кода, которые будут попеременно включаться и выключаться одним лишь пробелом в первом (стартовом) из комментариев.Например:
/**/
Console.Write("1");
/*/
Console.Write("2");
/*/
Console.Write("3");
/*/
Console.Write("4");
/**/
Console.Write("5");
При выполнении этот код выведет в консоль строку
"135"
. То есть, будут выполнены все нечетные операторы вывода — и последний, находящийся уже за пределами всей конструкции. Но если в стартовом комментарии между второй звездочкой и слешем вставить пробел (или, строго говоря, любой символ, кроме звездочки), то тот же самый код выведет строку "245"
: будут выполнены только четные операторы, и, опять же, последний, который уже за пределами. (UPD: благодарю FluffyMan за указание на ошибку).Синтаксис комментариев-разделителей предельно строг: к конструкции
/*/
ничего ни убавить, ни прибавить нельзя, это разрушит ее функционал. Синтаксис стартового и стопового комментария напротив совершенно произволен. Он может быть минималистичен до /**/
, а может содержать какие угодно — легальные в смысле языка — комментарии. Откуда ясно, что стартовый и стоповый комментарии строго обязательны, и что внутри самой конструкции просто так использовать легальный комментарий типа /* ... */
нельзя, т.к. он тут же станет стоповым для всей предыдущей последовательности комментариев-разделителей /*/
, и стартовым для всей последующей их последовательности. Но осмысленное использование таких вставок может оказаться полезным.Однострочные комментарии
//
на функционал не влияют.Dixi :)
Комментарии (33)
AllexIn
25.12.2018 13:50+4Краткое пособие «Как выстрелить себе в ногу на ровном вместе».
UPD:
Ну вот, мой остроумный коммент оказался мало того что не уникальным, так еще и не первым…pythe Автор
25.12.2018 16:42-3Чем поддержка такого кода принципиально отличается от традиционного
#define DEBUG
#ifdef DEBUG
…
#else
…
#endif
#ifdef DEBUG
…
#else
…
#endif
?iroln
25.12.2018 16:49+1Тем, что комментарий надо искать и руками править, а DEBUG и любые другие дефайны можно конфигурировать на этапе конфигурирования/сборки, а также о них и их состоянии чаще всего знает ваша IDE. Это очевидно же.
pythe Автор
25.12.2018 17:04-2Понятно. Разумеется выключка комментированием предназначена исключительно для относительно «легких» текстов, и желательно IDE с подсветкой. Это верно для любой подобной техники. Но в «тяжелых» тоже попадалось.
Vantela
25.12.2018 14:18Я в школе делал так — создавал int DEBUG=1; и перед операторами которые нужны для дебага писал if(DEBUG){...}.
Мой подход не идеален, но ваш ужасен.
Переделывайте лучше на #ifdef. Пока не поздно.
GennPen
25.12.2018 14:18С такими комментариями потом бегай и ищи по коду что забыл включить/выключить.
Нет уж, лучше по-старинке:
#ifdef DEBUG
#endif
Bedal
25.12.2018 14:21+2Эхх, вспоминается… М-222, ТА-1М. Программа прогонялась в один приём: загоняется колода перфокарт, транслируется и тут же выполняется. Получается единая распечатка кода программы и вслед за ним — результатов выполнения.
Печаталось это на барабанном принтере, который интерпретировал CRLF как «очистить буфер»/«продвинуть бумагу на строку».
Студенческая работа на алголе. пишем строку кода, потом слово comment, потом пробел и комментарий, очень похожий на код, но неправильный. Строка заканчивается нормальным crlf, ессно. Получив колоду перфокарт, отыскиваем нужный столбец и перевырезаем лезвием пробел на символ CR.
Итого: на печати начальная часть строки и слово comment не появляются, так как буфер принтера очищен (cr). Зато неправильный код вот он, на печати, как родной.
Сдаём в работу, получаем распечатку, несём преподавательнице.
— Незачёт.
— Почему?
— У вас в программе грубые ошибки.
— Не может быть, вот, ниже ведь результат. Он правильный?
— Да, правильный. Но так быть не может.
— Но вот же распечатка. Транслятор ошибок не выдал, программа выполнилась без ошибок и выдала правильные результаты — почему незачёт?
— …
было, развлекались пару раз…
_________________
Связь с постом — вроде бы понятная?pythe Автор
25.12.2018 16:57Не очень :)) И как можно пробел (20) перерезать в CR (0D)?
Лезвием дырки в перфокарте сверлить — мда, работа для ювелира-труженика :) Эх, времена… Я в немножко похожей ситуации пользовался автоматическим переносом слишком длинной строки. Принтеры были уже матричные.geher
25.12.2018 19:17И как можно пробел (20) перерезать в CR (0D)?
Зависит от кодировки перфокарт, а она была разная.
В некоторых случаях пробел кодировался как раз отсутствием пробивок.
В КПК-12 (иногда неправильно назывался ДКОИ-12, а 12 потому, что каждый символ кодировался пробивками или их остутствием в 12 строках) вроде оно невозможно (если не путаю), поскольку код пробела имел отверстие, которого не должно было быть в CR
Лезвием дырки в перфокарте сверлить — мда, работа для ювелира-труженика
Это да. У нас специальный пробойник имелся.
yurrig
26.12.2018 05:27Лишние дырки затыкались картонными прямоугольничками, которые после пробойника оставались. Часто так было быстрее и проще поправить ошибку, чем ждать, пока тебе программу перепробьют. Лезвие и пакетик с затычками было наше все…
Bedal
26.12.2018 07:10именно так :-)
пакетик с затычками
который наполнялся, когда удавалось оказаться возле Бармалея (перфоратора перфокарт).
geher
26.12.2018 10:45Отож, лезвия недостаточно.
А мы лишние дырки заклеивали, потому что просто вставленные обратно прямоугольники часто вылетали.
Bedal
26.12.2018 07:17пробел (20)
ну, положим, не 20, а 40
перерезать в CR (0D)?
как и написал ув. yurrig, лишние дырки заклеивалась, нужные — прорезались. Одной дырочкой не обходилось.pythe Автор
26.12.2018 10:41ну, положим, не 20, а 40
Я там в hex выражался, а не в oct. Вспомнил позже, что да, пробелы были вообще без пробивок. А вот какими были коды CR ил LF не помню совсем.Bedal
26.12.2018 10:55Я там в hex выражался, а не в oct
уфф. Какой такой hex в М-222? Восьмерично-десятичные символы. И вообще, речь про перфокарты — со своим кодом. Кстати, точно уже не помню, в каких позициях что было. Больше 40 лет прошло, всё-таки. А подделывать гугленьем не хочу.
geher
26.12.2018 11:01Тут вам не ANSI. Так что именно 0х40 (ДКОИ-8).
А пробел в разных перфокартных кодировках разным был.
В КПК-12 пробел вроде был 12-9-8-5, а CR — 8-4 (могу путать, давно прошли времена, когда перфокарты по дырочкам мог читать).
Capacitor10n
25.12.2018 16:25Не более 2? Оо
#define mode 0 #if mode == 0 #elif mode == 1 #elif mode == 2 ... #elif mode == 999999999 #endif
Keremet_2030
26.12.2018 17:27Я иногда использую подход через case, делаю глобальную переменную DebugMode = n;
case DebugMode of
0: Begin
//// код 1
End;
1: Begin
//// Код 2
End;
2: Begin
//// Код 3
End;
PS: Простите за паскальGennPen
26.12.2018 23:22Мне кажется такая структура (в отличии от директив препроцессора) не очень хороша, т.к. все в блоке будет компилироваться в программу, хоть и не будет доступна для выполнения. Только если оптимизатор во время компилирования не выкинет недоступный код.
FluffyMan
25.12.2018 23:05Но если в стартовом комментарии между второй звездочкой и слешем вставить пробел
может между звездочками? иначе тогда не понимаю как это работает.pythe Автор
26.12.2018 09:53Оу, кажется, единственный комментатор, который обратил внимание на механизм :)
Нет, пробел между звездочками ничего не изменит, именно перед слешем. Это превращает */ в * / и дезавуирует последовательность как завершающий комментарий токен. Таковым становится следующий в очереди /*/, который до этого наоборот начинал следующий блок комментария, и т.д. по цепочке до завершающего /**/. Лучше всего это наблюдать в IDE с подсветкой синтаксиса.FluffyMan
26.12.2018 11:37Понял. Но тогда у Вас перепутано. Ведь этот код:
/**/ Console.Write("1"); /*/ Console.Write("2"); /*/ Console.Write("3"); /*/ Console.Write("4"); /**/ Console.Write("5");
выведет 135, но после вставки пробела между звездочкой и слэшэм уже будет 245:
/** / Console.Write("1"); /*/ Console.Write("2"); /*/ Console.Write("3"); /*/ Console.Write("4"); /**/ Console.Write("5");
P.S. <source lang="cpp"> помог)
Спасибо за разъяснение.Vantela
26.12.2018 11:43Перепроверил в vi.
Браво! Выходит автор выстрелил себе в ногу даже в статье в которой рассказывал как это сделать:)pythe Автор
26.12.2018 17:40Выходит автор выстрелил себе в ногу даже в статье в которой рассказывал как это сделать:)
:)))) Мда, выходит. Но заодно рикошетом зацепил целую кучу завсегдатаев гиков, которые из-за этого осерчали, и оказались не гиками, а нёрдами, и понаставили кучу минусов за несоответствие их идеалам. Вместо того, чтобы просто похихикать :)Vantela
26.12.2018 17:47Я не ставил.:)
Дело то не в идеалах.
Реально ж очень легко запутаться где там пробел. Потому и заминусили статью.pythe Автор
26.12.2018 18:00Да я ж не в претензиях :) Пусть их.
Никто не мешает использовать вместо пробела строчку DEBUG — эффект будет точно тот же самый. Равно как никто не мешает вместо #define DEBUG использовать #define d и запутаться не меньше. Дело вкуса, а компилятору пофиг.
pythe Автор
26.12.2018 17:31Но тогда у Вас перепутано.
Так точно. Этот самый попутал, который с рожками. Спасибо за поправку. Внесу Вашу коррекцию в текст.
Andy_Big
26.12.2018 14:09Однако такая конструкция не позволяет создавать более двух альтернативных участков кода.
Директивами препроцессора (#ifdef, #if defined() и т.д.) можно создавать сколько угодно альтернативных участков кода. У меня, например, поддерживается около 18 вариантов одной программы. Причем во многих IDE можно создавать отдельные конфигурации построения под каждый вариант и построить все или выбранные варианты в несколько кликов мыши :)
Gedweb
Такой код будет невозможно поддерживать, вы стреляете себе в ногу и называете это лайвхаком.