Ознакомьтесь с нашей политикой обработки персональных данных
  • ↓
  • ↑
  • ⇑
 
Записи с темой: говнокод (список заголовков)
22:32 

Untitled [492]

Коллега, которому передали один из проектов, с которым раньше работал я, поставил себе PVS Studio. Статический анализатор кода. И он стал выявлять разные шняги, которые написал, как выяснилось, я (и это подтверждает SVN Blame).

Вот один из таких кусков, высранных мной в 2013 году.

int nx, ny, nresx, nresy;
char file_list[80][261];
unsigned stx[80], sty[80];
fscanf(f, "%d %d %d %d\n", &nx, &ny, &nresx, &nresy);
int nitems, nfiles;
for(nfiles = 0, nitems = 0; !feof(f); nitems++, (strcmp(file_list[nfiles], "-") && (file_list[nfiles][0] != '\0')) ? nfiles++ : 0/*иначе ничего*/)
{
fgets(file_list[nfiles], 261, f), file_list[nfiles][strlen(file_list[nfiles]) - 1] = '\0';
if(strcmp(file_list[nfiles], "-"))
{
fscanf(f, "%d %d\n", &stx[nfiles], &sty[nfiles]);
}
}

//nitems--;// |---+----\_______. КОСТЫЛЬ
//nfiles--;// |---+----/ КОСТЫЛЬ


Что тут происходит конкретно, я уже и сам толком не помню. Предлагаю попробовать догадаться самостоятельно. f -- текстовый файл, откуда что-то считывается.

Интересно тут следующее:
1. Заголовок цикла for содержит не только операцию "запятая" в инициализаторе, но и в повторяемом выражении (третья часть).
2. Первый оператор цикла также содержит в себе операцию "запятая".
3. Комментарий с костылём смешной. Но почему-то закомментирован.
4. PVS Studio ругался не на стиль, а на то, что я условное выражение в операции ?: не обернул в скобки (хотя функционал от этого не меняется, т.к. у && приоритет выше, чем у ?:).

Зачем же были нужны операции запятая в таком количестве? Она тут только ухудшает читаемость. У меня только одна гипотеза. Изначально в теле цикла был только первый оператор. Когда ситуация усложнилась, я не захотел вводить новый блок, поэтому использовал всевозможные извращения, ТОЛЬКО БЫ обойтись одним оператором в теле цикла. Но в итоге сдался. А то, что уже запихнул в заголовок и в операцию "запятая", доставать обратно не стал. Эх...

@темы: Программирование, Говнокод

23:56 

Докажи, что ты не верблюд

Есть два измерительных модуля в одном из приборов, которые мы делаем -- Module1 и Module2. Меряют разные характеристики. Один давление, другой температуру.

Разные экземпляры прибора могут быть укомплектованы разным набором модулей. И вот данный конкретный экземпляр имел только Module2. И его эксплуатировали 4 месяца не включая этот модуль. Но понадобилось включить. И оказалось, что модуль не работает.

Начинаю отлаживать ПО. И вижу картину маслом:

void InitModule2()
{
if(Settings.bModule1Installed==false)
return;
//далее инициализация модуля2
}


То есть: если в приборе НЕ установлен модуль 1 (а он и не был установлен), прекратить инициализацию модуля 2! Модули более-менее независимы. Это меня удивило. Я сделал svn blame. Эта команда позволяет установить, кто является автором каждой строчки кода (и у неё есть синоним -- svn praise, лол). В результате этого я выяснил, что данную строчку написал начальник. Как раз 4 месяца назад. И тогда же обновляли ПО на данном приборе.

Я подумал, может быть в этом есть глубокий смысл? Спросил его. Но оказалось, что это всего-навсего последствия невнимательного копипаста при рефакторинге. В первом модуле текст был правильным. Инициализация отличалась несильно. Поэтому начальник просто скопировал текст инициализации первого модуля и подправил нужные места. Но не все...

@темы: Борьба с техникой, Говнокод, Программирование

23:59 

Адресная книга

Обнаружил у себя в закромах родины собственноручно написанный эмулятор калькулятора МК-52 под калькулятор же МК-85. В виде листинга на пяти листах. Написанного от руки.

И вспомнил, как я его писал. Ещё в школе.

МК-52 -- программируемый советский микрокалькулятор. Он программируется в шестнадцатеричных кодах. Для их ввода можно воспользоваться клавиатурой, на которой написаны разные действия. Это помогает вводить программу и записывать на бумагу. На бумаге пишут, к примеру:

1 2 В↑ + С/П

Это значит, сложить 1+2 и закончить программу.

А в памяти (и встроенном просмотрщике программы) это отображается так:

50 10 0E 02 01 (читать надо справа налево)

МК-85 более крутой, он поддерживает Бейсик. Писать эмулятор на Бейсике извращение ещё то. Нет, место, где хранить программу в кодах -- есть. Вопрос, как её выполнять?

Допустим, в переменной D хранится код очередной операции. И её надо выполнить. Что же делать?

Логика подсказывает простой дедовский способ:

100 IF D=0 THEN ...
101 IF D=1 THEN ...

И тут меня ждала засада. В двух частях:
1. В МК-85М всего 5317 байт (!) памяти. Да, бейсик представляется в байт-коде, т.е. каждое ключевое слово идёт как 1 байт. Но всё равно памяти мало.
2. Скорость выполнения программ будет просто умопомрачительной, т.к. чтобы выбрать правильное действие, надо пробежать в среднем по половине проверок. А работает калькулятор так, что добавка даже 2-3 строчек, выполняющих простые операции -- заметна.

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

Потом я прочитал интересную вещь. Оказывается в Бейсике МК-85 команды GOTO и GOSUB (подпрограмма) могли принимать не только число. Но и переменную. И даже выражение.

Это и был ключ к тому, как всё уместить в 5317 байт.

100 GOSUB D+1000
//...
1000 (действие1):RETURN
1001 (действие2):RETURN
//и так далее для всех ~200 кодов операций

@темы: Программирование, Говнокод

23:58 

Коллекционер стеклотары

Появилась необходимость сохранять настройки в энергонезависимой памяти микроконтроллера. МК STM32. У большинства моделей отсутствует EEPROM, специально для этого предназначенный. Поэтому остаётся только одно -- хранить в основной флеш-памяти, где прошивка.

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

Забиваю в гугл "работа с флеш-памятью stm32". Передо мной появляется 10+ статей (и я наверное напишу ещё одну). Во всех написано примерно одно и то же. Но интересовал меня изначально в статьях строго определённый момент: как определить, где заканчивается прошивка, чтобы использовать под свои данные свободное место?

Но во всех статьях этот вопрос решался одним и тем же образом: предлагалось писать в последние адреса памяти, т.к. там шанс наткнуться на прошивку наименьший! Самоуверенность необыкновенная.

Потом я нашёл одну (!) статью, где интересующий вопрос освещался не с позиции рандома.

@темы: Программирование, Говнокод

23:59 

Опасность enum

enum -- что может быть лучше?

(для тех кто не в теме)

Чем же это опасно? При любом изменении списка, исключая добавление новых констант в конец, меняется фактическая нумерация! И значение, которое было раньше 2, теперь становится 3. Это совершенно некритично, если доступ к переменным завязан ТОЛЬКО на применении символических значений. Как только происходит выход за пределы данного лягушатника -- начинается беда-печаль. Вот примеры:
1. Бинарное общение с другой программой, у которой список констант немного отличается.
2. Общение самого с собой через файлы. Сохраняем, к примеру, настройки в файл. Обновляем программу, файл считывается, а значение уже не то!
3. Использование символических констант в качестве индексов предопределённых массивов. Например:

enum TypeName {SYMBOLIC_CONSTANT1, SYMBOLIC_CONSTANT2, SYMBOLIC_CONSTANT3};
char SymbolicNames[][80]={"Name1", "Name2", "Name3"};

printf("Name: %s\n",SymbolicNames[SYMBOLIC_CONSTANT2]);

Если модифицировать первый список, по индексам будут выпадать чужие имена.

Что с этим делать:
1. Не модифицировать список в середине и в начале. Никогда. (иногда может показаться, что это можно сделать безболезненно... переупорядочить константы по группам, к примеру)
2. Присваивать принудительные номера. Всегда. (как это делать, см. под катом в начале)
3. Использовать #define .

@темы: Программирование, Говнокод

23:58 

Как провести рефакторинг кода и не облажаться?

За этим громким названием скрывается всего лишь один трюк. Он применим только к одной очень конкретной ситуации, которая, как мне кажется, не совсем подходит под определения "рефакторинга".

Но раз уж я написал заголовок, я, пожалуй, всё же отвечу на него.

Так как же провести рефакторинг кода и не облажаться? Ответ: никак.

Но кое-от-чего защититься можно.

Итак, пусть у нас в программе есть об'ект A. Нам надо добавить новый об'ект -- A2, того же класса. Из текущих ситуаций использования A половина должны остаться за ним, а половина -- перейти к A2 (правила определения, кто чем будет заниматься, известны заранее). Ситуации встречаются по всей программе. Об'ект практически глобальный. Как же технически провести эту работу?

Самое очевидное решение -- пролистать все употребления A и часть перевести на A2 (предварительно создав его). Но так есть риск пропустить что-нибудь, даже если использовать глобальный поиск.

Я предлагаю способ, благодаря которому пропустить ни одного употребления просто не выйдет. Об'явление об'екта A надо исключить из программы! Либо удалить A, а вместо него создать массив A0[2], либо переименовать A в определении (и только там!) в A1.

К чему это приведёт? Программа не будет компилироваться из-за употребления несуществующих переменных. Придётся исправлять ошибки...

Хотя и тут есть возможность сфейлиться. В какой-нибудь функции может быть одноимённый локальный об'ект того же класса. Он раньше перекрывался A, а теперь -- нет. В этом случае ошибки не будет.

@темы: Программирование, Лайфхак, Говнокод

23:59 

И прочие ингредиенты

Есть древняя-древняя функция puts. Она выводит на экран строку. И переводит каретку на новую.

У меня был список чисел, надо было записать в файл. При работе с текстовыми файлами я и в Си и в пхп предпочитаю олдскульный способ -- fopen/fcolse, fprintf/fscanf и прочее.

И вот я зачем-то решил воспользоваться fputs. Не знаю, зачем.

Короче говоря совершенно неожиданно я выяснил, что fputs символ новой строки в конец не добавляет. А обнаружил я это узрев сплошную строку цифр в выходном файле.

Это одна из плохих вещей в Си: что функции разных семейств, делающие по сути одно и то же, различаются в существенной особенности.

@темы: Программирование, Говнокод

23:55 

Критическая уязвимость CloudFlare: diary.ru тоже в опасности

CloudFlare -- это такая фирма, которая предоставляет миллионам сайтов услуги по перераспределению нагрузки к ним, защиты от ддос-атак и прочее. Многие, наверное, видели страницу "КлаудФлейр проверяет ваш браузер" или "КлаудФлейр не смог достучаться до сайта (ошибка 522)".

Так вот:
github.com/pirate/sites-using-cloudflare
Официальное сообщение:
blog.cloudflare.com/incident-report-on-memory-l...

Если коротко, то в течение последнего полугода данные с 4 миллионов сайтов, которые используют CloudFlare, потенциально могли утекать в неизвестном направлении, включая ваши пароли, сообщения и прочее. И через кэши поисковых систем часть данных ещё может быть доступна третьим лицам.

На практике это означает, что надо поменять пароли на всех сайтах, которые используют эту систему. Их список в запакованном виде занимает 22 мегабайта. Вот он, упорядоченный по алфавиту:
github.com/pirate/sites-using-cloudflare/archiv...

Обращаю внимание, что надо смотреть на точное совпадение доменов. То есть, gmai-l.com, yandex.info -- это всё левые домены, не имеющие отношения к жмейлу и яндексу.

Что важно -- так это что сайт diary.ru использует CloudFlare. С учётом написанного выше, мне кажется, очевидно, что надо сделать в связи с этим. Интересно, что администрация сайта про это ничего не сообщила.

Кроме того, это означает, что ВСЕ сведения, передаваемые вами через эти сайты в течение последнего полугода, могут оказаться в руках неопределённого круга лиц, помимо адресата, провайдера и ФСБ.

@темы: Фейлы, Программы, Говнокод

23:58 

Что посеешь, то и пожмёшь

Одним проектом я не занимался год. Потом открыл. Ох, как всё запущенно.

Я знал и знаю некоторые правила хорошего стиля программирования, которые я сам иногда нарушаю. И открыв этот проект я огрёб по полной. Я не знаю, почему я тогда нарушил парочку базовых правил, но сейчас мне это сильно добавило работы. Правила все давно знают, но всё же их укажу:

1. У переменных должны быть понятные имена. Это касается не только переменных типа _, asdbsd и Burrrrp (хотя игроку в НетХак последняя будет весьма понятна) -- тут всё ясно. Название может иметь отношение к реальной ситуации, но имеет шанс быть воспринятым неправильно. Так, у меня в программе была переменная по имени need_count. Я долго думал, что это число раз, которые надо (need) посчитать (count) что-то. Однако на самом деле эта переменна обозначало то, НАДО считать (1) или НЕ НАДО (0).

2. Отсюда вытекает второе правило -- тип переменной должен соответствовать её назначению. Переменная из п.1 имела тип int. Хотя гораздо понятнее всё было бы, если бы я сделал её типа _Bool (это было в той части проекта, которая работала на микроконтроллере, а она написана на голом Си-99, поэтому именно _Bool, а не bool). Для МК, это, правда, простительно, т.к. int будет быстрее обрабатываться в ряде случаев.
Примечание. Беззнаковые типы при совместном использовании со знаковыми таят в себе гремучую бомбу: zhz00.diary.ru/p211118163.htm

3. А третье правило -- не следует повторно использовать ту же переменную для других целей. Хотя для переменных типа x, y, i особой разницы нету, сколько раз их использовать. Но повторное использование более специальных переменных часто ведёт к нарушению п.1. То, на чём конкретно напоролся я -- WriteFile возвращает через указатель число записанных байтов. Для этого я использовал переменную с подходящим именем written. Но потом мне надо было читать данные при помощи ReadFile. Как не трудно догадаться, он тоже через указатель возвращает число байтов, но уже прочтённых. Видимо тогда я решил сэкономить четыре байта. Ну вот нафига? Да-да, именно так. Я написал --

ReadFile(hFile,size,&written,NULL);

Потом очень удивился -- зачем я после чтения проверяю число записанных байт?.. Это уже было в части программы для ПК, так что тут прощения мне нет -- под ПК экономить нечего.

Это я тогда был не в своём уме или так вырос за год, что считаю дикостью то, что сделал своими же руками?

Но кое-от-чего чего я пока избавиться не смог -- так это от применения операции ?: . Очень её люблю, хотя она сильно усложняет чтение текста.

@темы: Программирование, Очевидное-невероятное, Говнокод

23:59 

Как не надо обрабатывать ошибки

Если у вас обработка ошибок сделана через коды ошибок, то должны соблюдаться следующие правила:
1а. Код, возвращаемый из каждой ветки управления, должен быть уникальным.
ЛИБО
1б. При выводе сообщения об ошибке должен указываться не только код, но и место возникновения ошибки (файл с исходным текстом, строка; в си/пхп для этого есть __FILE__, __LINE__, а в пхп ещё и __FUNCTION__).

2а. Сообщения об ошибках должны быть уникальными.
ЛИБО
2б. В сообщения должен быть включён код ошибки.

Я же встретил следующую вещь:
switch(nError)
{
//...
case 100:
case 101:
case 102:
AfxMessageBox("Разгерметизация скафандра!");
break;
//...
}


А ошибки 100, 101 и 102 выпадали из десяти разных мест.

После этого разработчикам становится понятно, в чём дело, но становится непонятно, куда копать.

@темы: Говнокод, Программирование

22:49 

>>_<<

Коллеги из соседнего отдела прислали исходники своей проги на PHP. Начинаю их изучать.

Программа для Raspberry (Pi?). Она подаёт на 4 вывода GPIO число. То есть число разбивается на 4 бита, и каждый из битов управляет одним из проводов. Если бит равен нулю, на проводе 0 вольт, если 1 -- 3.3 вольта (или 5 вольт, не разбирался).

На, значит, разбить число на отдельные биты. Как же выделить определённый бит? Я вижу следующий текст:

$bit3=$number/8;
$bit2=($number-$bit3*8)/4;

И так далее. Это, конечно, работает, но меня сильно удивило. В PHP работают побитовые операции, включая обычные сишные >>, << и &.

@темы: Программирование, Говнокод

23:21 

На долгую память о старых друзьях

Я отлаживал программу. Программа работала на компе с подключённым прибором. Возможности поставить туда среду разработки или удалённый отладчик не было.

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

Спустя 4 дня отладки я обнаружил наконец, в чём заключалась ошибка в коде. Вот отрывок. Зависание иногда происходило где-то в нём, причём один раз на 10-20 выполнений.

bFlag=GetStatus();
fTime=0;
while(!bFlag&&fTime<0.5);
{
//do something
fTime=TimeElapsed();
bFlag=GetStatus();
}


Вписать в само условие GetStatus() по некоторым причинам было нельзя (я привожу тут текст упрощённо). Функция TimeElapsed() возвращает, сколько прошло времени с начала ожидания. Если прошло 0.5 секунды, дальше мы начинаем сушить вёсла, продолжение нормальной работы невозможно. Функция GetStatus() проверяет состояние другого потока в многопоточном приложении -- не возникло ли в нём определённое событие? Если статус равен фолс (не возникло), ждём дальше. Если тру (возникло) -- выходим. Специфика её работы такова, что вернувши один раз тру, дальше она начинает возвращать фолс -- до следующего возникновения события во втором потоке. Я долго грешил на ошибки синхронизации, тыкал палочкой в критические секции -- безрезультатно. Пока не обнаружил при помощи отладочной печати странную вещь. bFlag до цикла равно тру, а внутри цикла -- фолс. В тех случаях, когда программа не зависала. Это значит, что ГетСтатус вызывался два раза. Это невозможно, т.к. Если он равен тру, в цикл вход происходить был не должен -- условие сразу ложно! И тут я присмотрелся повнимательнее...

***


Когда я ещё не имел опыта программирования на Си, классе так в восьмом-девятом, я всё же написал одну довольно об'ёмную по тем меркам программу. Но она не работала. Это была компьютерная игра. Основной цикл был прост, как число 3. Прорисовать экран -- дать пользователю сделать ход -- если конец игры достигнут, сделать выход. И с начала. Проблема была в следующем месте:

1	while(1);
2 {
3 Draw();
4 GameMove();
5 if(GameOver())
6 break;
7 }


Это плохой стиль. Не рекомендуется делать бесконечный цикл в тех условиях, когда чётко понятно условие выхода. Это допустимо делать, когда этих условий много и они сложно комбинируются.

При отладке наблюдалась странная ситуация -- после строчки 1 программа зависала. Я нажимал "следующую строку", но она продолжала висеть на этой. Кроме всего прочего выдавался варнинг -- unreachable code на строках 3-6. Это было очень странно. Я же всё чётко написал! Строчка должна выполняться. Я искал ошибку полгода, с перерывами. Потом до меня допёрло -- в первой строке в конце стояла точка с запятой. Это и было тело цикла. Т.е. это был бесконечный пустой цикл. И он бесконечно выполнялся, не переходя к строчке 3.

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

Вообще первый отрывок написан очень плохо. Его надо было тоже делать с постусловием.

@темы: Программирование, Говнокод, Борьба с техникой

23:59 

Zero no Tsukaima

Студенты учат Си/Си++ по разным книжкам. Я их иногда разглядываю. Сегодня разглядывал книжу Костюкова, Калинина // Язык Си и особенности работы с ним. Разглядывал долго. Авторы -- две женщины глубоко пенсионного возраста. Само по себе это может быть и неплохо -- типа люди старой советской закалки, суровые женщины-программисты... В целом книжка мне не понравилась. Но одно место откровенно поразило:

/*Посчитаем среднее геометрическое*/
g=pow(a*b*c,(1/3));

pow стандартная функция из math.h и имеет прототип:
double pow(double a, double b);
Возвращает a в степени b.

Да я сам студентам постоянно даю задание на подсчёт того, чему будет равна 1/3. Я думаю -- может быть, это специально -- типа чтобы обратить внимание на приведение типов (а в Си 1/3 равно нулю, т.к. делится нацело -- частное 0 и 1 в остатке). Но нет -- никаких дополнительных комментариев не было.

@темы: Говнокод, Программирование, Студенты

23:57 

Десятичный сдвиг

Не подумайте, это не я писал.

double var1;
//...
// оставляем для вывода в поле только 2 знака после точки
var1 = double(int(var1*100.0))/100.0;

На самом деле это довольно остроумно -- кроме того, других методов понизить точность переменной я придумать не могу. И стандартной функции в Си не помню (кроме printf, но она даёт результат в виде текста).

Есть другие методы?

@темы: Говнокод, Программирование

23:56 

Одна стена -- огонь, другая -- лёд

В начале был цикл вайл:

n=n0;
while(cond)
{
//... что-то с участием array[n]
n--;
}

Потом планы изменились, и я переделал это в цикл фор:

for(n=0;n<n0;n++)
{
// то же самое
}

Потом думаю -- что-то цикл бесконечным стал.

ОКАЗАЛОСЬ

я забыл убрать n--;

Так он и принимал значения -- то 0, то -1.

@темы: Говнокод, Программирование

23:36 

Untitled [360]

О том, почему плохо применять одну и ту же переменную для двух разных задач.

Была одна процедура (процедура в смысле "логическое действие, которое должна выполнять программа", а не в смысле "функция в терминах языка программирования, которая не возвращает значения") и она имела, допустим, три режима:

#define MODE1 1
#define MODE2 2
#define MODE3 3

А у каждого из режимов было два подрежима:

#define MODE1A 1
#define MODE2A 2
#define MODE3A 3
#define MODE1B 4
#define MODE2B 5
#define MODE3B 6

И была переменная, которая хранила ОБА этих режима.

int nMode;

СНАЧАЛА там хранился общий режим и процедура работала с его использованием. Но, на определённом этапе (которой пришлось долго вычислять, т.к. процедура длинная) та же переменная начинала хранить уже ПОДРЕЖИМ. При этом диагностику затрудняло то, что числовые значения констант частично совпадали.

Не надо так.

P.S. Предвижу вопрос "Почему не сделали сразу просто хранение подрежимов, а на режимы бы просто не забили?". На самом деле режимов не три, а больше. А число подрежимов у каждого режима -- своё. На определённом этапе ветвление осуществляется по коду режима, а по коду подрежима -- потом. Поэтому разделение на "режим" и "подрежим" логично. Что нелогично -- так это одна переменная и под то и под другое. И что очень плохо -- что замена режима на подрежим происходила при вызове определённой функции ВНУТРИ неё, при этом замена производилась по ссылке:

void non_suspicious_name(int a, int b, int &mode)
{
mode=0;//...или что-нибудь ещё
}
//в другой функции
non_suspicious_name(val1,val2,nMode);
// теперь тут уже не режим, а подрежим

Это аргумент в пользу того, чтобы не использовать ссылки, а только указатели.

@темы: Говнокод, Очевидное-невероятное, Программирование

23:53 

Это Спарта

Дело было давно. Я просматривал код на Delphi, и мой взгляд упёрся в такую строку:

if(variable and 1=1) then // что дальше -- значения не имеет


У меня глаза по пять рублей. Боже, что это такое?! Один всегда равно один. Зачем это было писать?!

Оказалось, что у and приоритет выше, поэтому сначала выполняется variable and 1, а потом сравнивается с единицей. variable -- целая переменная, поэтому and -- побитовый. Это была проверка последнего бита.

@темы: Программирование, Говнокод

23:22 

Неизвестный источник

Я не помню, где и когда я прочитал (или услышал) следующее утверждение:
Если вы выделили память для массива или для одного элемента, лучше его всегда! удалять при помощи delete [], который сработает, даже если вы выделяли один элемент, например int *p=new int;delete []p;

Так вот, оказывается, это не так.

Выделил через new -- удаляй через delete.
Выделил через new[] -- удаляй через delete[].

Иначе поведение не определено.

@темы: Программирование, Говнокод

23:18 

wand of waiting (0:∞)


int t;
//...
while(!some_check())
{
cout << "\\|/-"[t=(t+1)%4]<<"\b";
wait();
//...
}


Вот это открытие. Оказывается работает индексация строковых констант! АААААААА!

@темы: Программирование, Говнокод

23:16 

Untitled [125]

Как нормальные люди пишут код, если им надо вызвать несколько функций подряд?

void a();
void b();
void c();
void d();

int main()
{
a();
b();
c();
d();
return 0;
}


Как пишут все остальные?

void a();
void b();
void c();
void d();

int main()
{
a();
return 0;
}

void a()
{
// тут какие-то действия
b();
}

void b()
{
// тут какие-то действия
c();
}

void c()
{
// тут какие-то действия
d();
}


@темы: Говнокод, Программирование

Untitled

главная