Pull to refresh

Comments 116

А что вы хотите, индийцам тоже нужно чем-то на хлеб зарабатывать.
Палец вверх за goto!
(Пародия на this is horosho)
Это означает скорее всего то, что программистов быстро переучивали с бейсика (или фортрана), причем с очень старого, где GOSUB было юзать не принято :)
В коде ядра Linux тоже есть goto. Все дело в том каким образом в коде на Си конкретное goto применяется и по умолчанию goto в Си говнокодом не является.
Если судить по другим мейнстрим языкам, то проблемы goto обходяться другими алгоритмичискими решениями. Согласитесь, все же 1 goto на 60 строк — это многовато…
Если он даёт прирост производительности и не создаёт уязвимостей, то говнокод это использование громоздких, медленных и тяжёлых конструкций вместо лаконичного и шустрого goto
еще пример где без goto не обойтись (по крайней мере я не вижу как) — выход из трех вложенных циклов. Зачем делать три вложенных цикла — другая тема)
Например завести флаг и проверять его в каждом цикле.
освобождение ресурсов — другой неплохой пример (в ядре линукса с той целью обычно и используется).
Один JMP заменится на цепочку JNZ/JNE. В принципе, конечно, не средство экономии…
интересно, надо будет проверить на досуге)
в основном это как стриптизерша — смотри, но не трогай.
Конечно если эти три вложенных цикла не размазаны на 2000 строк кода…
И если goto не перекрестные
Я скорее соглашусь с вами. Но я кода не видел этого проекта. Может быть что каждые 60 строк кода им надо освобождать ресурсы и это удобно делать в Си с goto. А за 60 строчек можно много ресурсов наполучать от ОС…
Кстати можно было бы в вашем анализаторе более детальный анализ goto сделать. А то для проектов на Си это актуально. По крайней мере в обзоре было написано только что их много, а еще бы хотелось знать где и почему в коде проекта они используются неправильно.
Правил с goto сейчас в PVS-Studio нет, так как сам по себе goto ошибкой не является. Конечно, легко сделать ошибку в запутанном коде с переходами туда-сюда. Но там даже человеку сложно понять, что задумывал автор и что получилось. Так что пока идей, как код с goto анализировать не было.

То что много goto, это я просто глазами заметил. А потом посчитал и вообще удивился.
Знаете, я тоже думал, что использование goto — плохой тон. Но потом понял, что это, пожалуй, самый изящный способ выхода из вложенных циклов в большинстве языков.
Точно. Вот в питоне нет goto – вместо него пользуют страшное… Exception'ы. Но это даёт ещё один уровень вложенности, что не есть хорошо.
UFO just landed and posted this here
В Java для этого есть break и continue с меткой на цикл, из которого выходишь или выполнение которого продолжается с новой итерации.
Кстати, очень полезная конструкция.
Хотя и ненаглядно, т.к. метка ставится перед началом цикла, а переход происходит в его конец.
это тот же goto, но более тупой и дающий море возможностей перепутать магическую тройку с магической двойкой или 3.14.

если вы понимаете, в набор какий опкодов компилится сишечный код, использование goto — более чем естественно

Он там для обработки ошибок вроде используется, т.к. в Си нет исключений. Фактически goto может пригодиться только для досрочного выхода из блока. Все остальные способы использования goto можно назвать быдлокодом.
>Он там для обработки ошибок вроде используется, т.к. в Си нет исключений.

в основном — да, для обработки ошибок, но на самом деле — где надо, там и используется, а не где мама разрешила.

и возврат в начало функции бывает и выход из цикла чуть дальше его конца (аналог for-break-else) и просто пропуск нескольких блоков когда.

хотя бы потому, что три уровня вложенности в функции на 20 строк — намного больший ппц, чем красивый локальный переход. вобщем, имеющий grep. да увидит.
UFO just landed and posted this here
если я не ошибаюсь — то это богиня покровительствующая программированию, одно упоминание ее имени в коде защищает проект от багов/лагов примерно на 75%
Надо переименовать и повторно прогнать PVS-Studio. Ошибок должно стать в 3 раза больше.
Это смотря во что переименовать. Если в "{<,=;" то ошибок и вправду станет значительно больше.
UFO just landed and posted this here
Средняя курсовая работа студента поместиться в одну такую :)
В исходниках Андроид — Java классы по 10000 строк кода. И это скорее всего нормально. Просто люди не хотят создавать лишних абстракций, лишних подпрограмм (функций) там где они не нужны. Они написали класс-сервис на 10К строк, который делает законченную вещь. Внутри его делить и абстрагировать видимо нет смысла. Экономия времени и денег на лицо.
Java классы по 10000 строк кода? Или класс, число строк в методах которого превышает 10000?
В первом случае, мне кажется, что это классический big ball of mud, хотя если они этими классами умудряются не нарушить SRP и ISP, то это здорово (хотя я думаю, что если программист решился на такое, то это подразумевает, что больше никто и никогда в этот класс не полезет).
Во втором случае — это абсолютно нечитабельное самоубийство. Потребуется немало времени тому, кто попытается разобраться, что же там происходит. И, думаю, что в этом случае это метод почти наверняка не покрыт тестами, так как тестировать такой метод невероятно сложно.
> Или класс, число строк в методах которого превышает 10000?
Я имел в виду «число строк каждого из методов».
Часто заглядываю в исходники API, такого класса пока не встретил, или это не в API? Можно хоть один пример?
Можно ссылку на исходники?
Мне лично слабо верится, что методы длиной >10000 строк нельзя декомпозировать, сделав код более читабельным.
Вобщем там все хорошо разделено в коде по ответственности классов. Например: имеем сервис окружения ОС, например activity manager. Он выполняется в виде класса, который позволяет делать RFC вызовы к себе, а внутри него уже 10к строк. Точно название класса я сейчас не скажу, в котором это видел. Но это точно правда. Там вобще все файлы с кодом «большие», не зависимо Java 'то или C/C++. Тесты там тоже есть. Например есть большой compatability suit (вроде так называется), его можно собрать вместе с самим Android и запустить на девайсе. Он тестирует все подсистемы окружения ОС, написанные на всех языках. Если все подсистемы отработали по тестам — то девайс совместимый.
Также можно заметить что в исходном коде окружения Андроид ничего не указывает на то что он писался с использованием каких-либо IDE. То есть файлов какой-либо IDE или автоматически сгенерированных ими замечено не было. Там еще в системе сборке (написанной на make, руками) есть генераторы разных файлов.
Не, там классы вместе с реализацией методов на 10к строк.
Хорошо, убедили. Давайте будем писать весь код в одном классе. И функций пусть тоже будет поменьше.
Ну это дело каждого. Главное чтобы в команде согласие было.
бох мой — думаю, эти классы в 10 000 кода и не тестируются. У меня был проект, где правые английские парни наколбасили таких классов под 20 000 строк. Мы с командой такой проект два года переделывали, чтобы он хоть как то тестировался автоматизированно.
Попробуйте их посопровождать.
UFO just landed and posted this here
Тоже верно. Но там всего 75к строчек кода и из них 5 функций по 2 и более тыщи строк. Т.е. получается, что в этих функциях помещается не много не мало но 1/7 часть кода.
Часто после такого разбиения ради разбиения ВНЕЗАПНО оказывается что код можно использовать во многих местах. А потом его ещё раз разбиват, и ещё. Выделяют из него классы, хелперы… И оказывается что этот код на 2000 строк состоит из цепочки 3-х методов по 15 строк, и 10 сторонних методов и классов, которые используются в разных местах системы и в общем-то универсальны. Это и называется рефакторинг.
UFO just landed and posted this here
«Большой switch плюс куча ifdef» почти всегда красиво решаются правильной иерархией классов и полиморфизмом.
UFO just landed and posted this here
А кто говорил, что использование ОО кода будет простым и интуитивным? :) Бонус ООП скорее в гибкости и упрощении разработки/поддержки кода, а не в простоте его использования. А для упрощения уже каждый может добавить обёрток под свои нужды.

Больших switch и т.п. иногда можно избежать с помощью более высокоуровневых инструментов, например, вместо ручного парсера использовать генераторы. Опять же, не факт что получившийся код будет проще, но его будет легче писать и поддерживать за счёт более ограниченного и чистого синтаксиса.
UFO just landed and posted this here
Я же специально разделяю внутреннюю поддержку кода и его внешнее использование. На вашем примере restkit, снаружи было бы проще позвать одну функцию с нужными параметрами, чем городить непонятные объекты. Изнутри же более удобно этим управлять, когда код чётко разделён по классам, плюс это более гибко.

Парсер я привёл лишь в качестве примера высокоуровневого инструмента, упрощающего код для конкретной задачи, ничто ведь не мешает использовать что-то аналогичное и для выполнения байткода.
На счёт ifdef и парамаетров компиляции. Приведу пример: у нас есть класс CFileOperations с десятком методов по копированию, удалению, перемещению файлов и т.д. Класс должен работать под Linux, Windows и MacOS. Поэтому в каждом методе есть 3 блока вида «ifdef WIN… end if». Класс огромен и страшно читаем.

В тоже время можно было бы создать интерфейс IFileOperations и 3 реализации под разные ОС. В итоге:
-у нас на весь код был бы только 1 блок «ifdef»
-при добавлении методов количество «ifdef» не увеличивается
-в каждом классе собран код только под одну ОС — читать легко и приятно
-если мы забудем после добавления очередного метода написать реализацию под определенную ОС — нам об этом скажет компилятор, а не загадочное поведение на рантайме.

Я, конечно, не отрицаю случаев когда такой подход не катит, но частенько — катит.
UFO just landed and posted this here
К сожалению, с питоном не знаком, поэтому дальше судить не берусь. Вполне вероятно, что там и вправду без этого никак.
UFO just landed and posted this here
Чем больше функция — тем сложнее понять что она делает. Чем сложнее понять — тем больше вероятность, что при ее изменении будет допущена ошибка. Разбивать имеет смысл хотя бы ради этого.
Чтобы было легче ориентироваться в этих функциях, они используют заметные комментарии :-)
/*
 @@@@@   @@    @@@@   @@@@
   @    @  @  @    @ @    @
   @   @    @ @      @
   @   @    @ @       @@@@
   @   @@@@@@ @  @@@      @
   @   @    @ @    @      @
   @   @    @  @@@@  @@@@@
*/

//-------------------------------------------------------------------------
//   @    @    @ @@@@@  @   @
// @ @ @  @@   @   @     @ @
//  @@@   @ @  @   @      @
// @ @ @  @   @@   @     @ @
//   @    @    @ @@@@@  @   @
//-------------------------------------------------------------------------

Супер :)

	/*
	@    @   @@   @@@@@  @    @        @       @@@@   @@@@  @@@@@
	@@  @@  @  @    @    @@   @        @      @    @ @    @ @    @
	@ @@ @ @    @   @    @ @  @        @      @    @ @    @ @    @
	@    @ @    @   @    @ @  @        @      @    @ @    @ @@@@@
	@    @ @@@@@@   @    @  @ @        @      @    @ @    @ @
	@    @ @    @   @    @   @@        @      @    @ @    @ @
	@    @ @    @ @@@@@  @    @        @@@@@@  @@@@   @@@@  @
	*/
UFO just landed and posted this here
в первую очередь своим размером
А еще попробуйте нанимать не неё юнит-тест. :)
Наверное тем, что цикломатическая сложность высокая, и как следствие гарантировать отсутствие багов в ней невозможно
А вот могу тоже привести кусочек C#-говнокода, обнаруженный в коде реального проекта компании, известной и старой не менее, чем Интел.

И это не прикол — это реальный код.

string s = Request.QueryString["action"];
// s may have "remove" or "restore" values

switch (s[2])
{
case 's':
case 'S':
..............
break;

case 'm':
case 'M':
..............
break;

}
Можно еще оптимизировать:

char c = Request.QueryString["action"][2];
// c may have 'm' or 's' values, lol

switch ©
...
Особенно если Request.QueryString["action"] может вернуть пустую строку.
Надеюсь вы НЕ отправили баг-репорт! Индийский говнокод вряд ли что исправит, а вы только нарушите тонкий баланс ошибок позволяющий программе работать.
Без этой картинки, пост был бы не полноценным :)

image
Разработчики Intel тоже говнокодят
… можно было бы завести для этого вот такой макрос "#define STRLEN(s) (sizeof(s) / sizeof(*s) — 1)"

Стоит ожидать топика?
Разработчики PVS-Studio тоже говнокодят

Прошу, попробуйте написать вычисление длины пути хорошо. А мы обсудим Ваше решение.
Легко. Вот смотрите:
CString str = _T(«Тут:\Какой-то\путь»);
int len = str.GetLength();
К стати, сам первый постебусь с неэкранированных слешей :)
Это Си++. На Си прошу пример в студию. :)
По-вашему терминальный нуль просто так придумали?
Мы на разных языках говорим? Зачем по-вашему '\0' помещают в конец строки?
А как Вы думаете, почему Ваши вопросы минусуют, а мои плюсуют? И еще подумайте, что должен возвращать макрос, называющийся STRLEN.
По-моему вы чересчур преисполнены самомнением для человека, который для вычисления длины строки советует использовать макрос
#define STRLEN(s) (sizeof(s)/sizeof(*s) — 1)
Я поясню свои вопросы: (Null Terminator) '\0' — маркер окончания последовательности символов, хранящихся в памяти и составляющих данную строку. Таким образом длину строки, хранящейся в памяти, можно определить как разность смещений начала строки и терминального нуля, относящегося к данной строке.
Для препроцессоров компиляторов C/C++ строка является единым токеном, поэтому невозможно вычислить длину строки at compile time, используя macro expansion trickery.
Как бы я реализовал «вычисление длины пути хорошо»? Пожалуй что так.
Написали бы сразу, что не знаете как работает оператор sizeof.
Ещё один. С чего вы взяли, что я не знаю как работает оператор sizeof?
Суть моих ответов сводится к тому, что НЕВОЗМОЖНО определить длину строки at compile time, что безуспешно пытается сделать уважаемый Andrey2008. Это можно сделать только at run-time, к примеру, с помощью strlen.
Это же строковая константа, и её размер компилятор знает.
Было бы невозможно, вот только уважаемый Andrey2008 успешно определяет длинну константной строки:
#define PL_FOLDER_STRING "C:\\productivity_link"
#define PL_PATH_SEPARATOR_STRING "\\"
#define PL_APPLICATION_NAME_SEPARATOR_STRING "_"
Господа, приношу свои извинения! В заблуждение ввело то, каким образом могут обрабатываться строковые литералы:

char c = *"Brown fox jumps over the lazy dog";

Отдельные извинения для Andrey2008! Не сразу обратил внимание, что приведённый макрос предлагается использовать для литералов.
Отнюдь не пытаясь оправдаться за весь Интел, упомяну, что данный SDK взят со страницы whatif.intel.com. Как известно, там лежат даже не прототипы, а так — программки, некоторым из которых когда-либо суждено стать прототипами. Многие наброски и черновики сделаны студентами-практикантами, которых уже вычеркнули из книги судеб.

Там кстати специально написано: «What if software were like this?» ;)

Впрочем, это ничуть не меняет сути поста: код должен быть красивым, надежным, и в обязательном порядке проверенным статическим анализатором. Например, Klocwork или PVS Studio :)
Я понимаю, что это полупрототипы. И что присматриваться к ним не стоит. Но не удержался, и изучил «Intel Energy Checker SDK Primary Technology Contacts»:

Jamel Tayeb, Applications Engineer. Jamel Tayeb is a software engineer in Intel's Software and Services Group. He has held a variety of engineering, marketing and PR roles over his 10 years at Intel.

Kevin Bross, Modular Systems Architect. I've worked for Intel for 20 years in a variety of engineering, marketing, and architectural roles.

Действительно, студенты-практиканты. :-)
Ну во-первых, переходить на дичности как-то совсем уж некрасиво… А во-вторых, ниужели вы думаете что System Architect в Интеле код пишет?
Не говоря уже о Applications Engineer :)
Я почти угадал — загадывал тысячу, ибо на C очень удобно обрабатывать ошибки, исползуя код вида:

int somefunc(...)
{
  if (something_erroneus)
    goto ERROR;
  ...
  do_something();
  ...
  if (something_else_failed)
    goto ERROR;
 
  return 0;

ERROR:
  ... // free the resources, null the output arguments etc.
 return ERROR_CODE;
}
Все таки лучше иметь один return в функции.
Стоп-стоп, без фанатизма. Не стоит воспринимать рекомендации буквально: если говорят, что лучше иметь одну точку выхода из функции, это ещё не значит, что нужно весь код писать так, чтобы в каждой функции был ровно один return. Правила, придуманные человеком, в этом смысле не эквивалентны законам физики, ибо есть возможность выбора, и ей нужно пользоваться с умом. Ну, и учитывайте специфику языков: что позволено Юпитеру C'шнику, то не позволено плюсовику, так сказать.
Как раз в обработке ошибок это удобно, так как можно выполнить действия по выходу из функции, файл закрыть или мьютекс разблокировать.
я вот недавно в книжно магазине видел экземпляр, полный догматический заявлений типа «Не возвращайте из функции NULL, кидайте эксепшны» и мотивировалось это только тем, что NULL нужно будет обработать(как будто на эксепшн можно забить). Жаль название и автора не запомнил.
На NULL действительно нужно будет проверять, и самое главное — не забыть этого сделать. А исключение «проверять» не нужно, в этом и преимущество.
А я загадал 1200 и таки почти угадал =)

А все потому, что по долгу службы мне приходится работать с одним адским проектом, который содержит в своем череве некоторого монстра, реализующего транспортный уровень. За все мои 15 лет айтишного прошлого я не видел говнокода страшнее и махровее чем там. Лес дефайнов, километры свичей, частокол goto — все можно найти там. Коду 20 лет и его приходится поддерживать и портировать на разные платформы. И это ужасно, скажу я вам. А еще ужаснее то, что заказчик считает что все прекрасно и ничего страшного в этом нет. Just a business…

Если описанный вами проект написан на C++, то я вам очень сочувствую: на предыдущей работе я попал в точно такую же ситуацию — и goto, и switch на три-четыре экрана, макросы, «ООП» на структурах (коих по одной на сущность — никаких абстракций и наследования), функции на сотни строк (а одна была даже на ~3800). Будущее выглядело примерно таким же. Но мы нашли общий язык с начальством: я написал заявление, и больше там не работаю, а занимаюсь любимым делом, ха-ха (:
Че-то Вы быстро сдались. Я тоже в подобных ситуациях бывал. Тут все просто — свой кусок начинаешь писать правильно, в тех местах, где он примыкает к другому коду — подправляешь тот код. Тесты, конечно же, контроль версий и т.д. Так тихой сапой за полгодика можно привести в чувство самый мертвый код.
Рефакторить без тестов получается не всегда, а тесты, в свою очередь, не всегда можно повесить на хреновый код(отличный тут пример — в веб приложениях каша из логики и представления). Так и работаешь, постоянно восстанавливая тонкий баланс проекта, после каждой добавленной строчки кода. Чувствуешь себя той самой бабочкой, взмах крыла которой, несет катастрофы, мор, отчаяние.
Да, именно так и делается. Ощущаешь себя мужиком с мачете в джунглях =) Вгрызаешься в код, начинаешь окультуривать. Ну и так потихонечку-полегонечку. Вот только все равно нет никакой гарантии что не вылезет какая-нибудь очередная бяка за день до релиза.
Тут ещё нужно чтобы мотивации хватило, иногда посмотришь на это всё и руки опускаются. Думаешь, мол, вот устроил же себе развлечение, а ведь можно было просто ещё чуть фигни добавить, никто бы и не заметил ;)
У меня не было полгода времени и столько же терпения. Тем более, это был fatality, а предпосылки были ещё до этого чудо-проекта.
Да, именно на С++. Но много и реликтовых кусков на Си. Причем «аффтар» сего творения до сих пор не упокоился работает в команде и продолжает писать. Самое жуткое, что вот я смотрю на его код 20-летней давности и тот, что он написал буквально вчера… ну никакой разницы! Он прямо начинает излагать говнокодом, представляете? Ладно, если вся эта трехомудия образуется в результате патчинга-перепатчинга и затыкания ошибок… но в данном случае она зарождается прямо изначально. Это выше моего понимания.

Мне страшно оттого, что некоторые люди за \d0 лет практики продолжают как ни в чем ни бывало писать ТАКОЕ. Поэтому я очень осторожно отношусь к стажу («он еще в 80х начинал»), как показателю опыта и качества.
Зато у вас есть работа. Может быть, еще на 20 лет…
У меня большие подозрения, что тот товарищ так же думал, когда писал этот код
Вообще, кто кодит на ASM и на C одновременно, их должно не покидать чувство, что это очень близкие языки. И C хорош тем, что он ближе к ASMу, чем, например, Pascal или Java. Так вот чем ближе к машинному коду, тем уместнее конструкция GOTO, ведь процессор по сути то и делает, что «прыгает» от инструкции к инструкции, с помощью тех самых GOTO (безусловных JMP и условных JE, JNE...). Очевидно, что во многих случаях GOTO позволяет сократить кол-во строк кода и соответственно процессорное время.
ну ладно, из них 10273 в ext/date/lib/parse_date.c.orig, сгенерированном re2c
блин, и еще 10273 в ext/date/lib/parse_date.c
так неинтересно)
Да ладно, все говнокодят. Другое дело, что одни потом свой говнокод исправляют, а другие нет. И ещё одни учатся писать правильно, а другие так и говнокодят до конца жизни.
UFO just landed and posted this here
Нет ничего удивительного, что в гигантах есть места для инду/ази/говно кода.
В своё время тоже сильно удивлялся способностям Senior' ов из Редмонда (название организации додумайте сами). Программируя на C++ люди, которые были ответствененны за инфрасруктуру, копировали refernces на объекты параметры методов, отчего в определённые моменты, например, падали ВСЕ тесты в проекте.
Потом весь TFS вставал на уши.

Вся эта история может быть вызвана как бюрократией (мы направляем Васю Пупкина на этот проект и всё тут) так и сложностью с людскими ресурсами ресурсами, так и проблемой условного апгрейда кадров (Вася Пупкин работает уже 20 лет у нас, давайте дадим ему Senior и отправим на проект с неизвестными ему технологиями).
Sign up to leave a comment.