Я вчера или позавчера прочел этот анекдот со швабрами, вентилятором и дирижаблем здесь, на пикабу, но не помню в нем зеленого существа справа и бассейн с веточками? посередине, что это и для чего нужно не подскажете?
В истории это был остров безумного учёного, по производству химер-монстров. А В идеальном случае, новому программисту, нужно было просто добавить бассейн с вениками ))
А там объясняется как этот бассейн помог бы всему делу? ну например швабры держат потолок, вентилятор сдувает газ, на дирижабле эвакуация, а бассейн с вениками для чего?
Ссылку и точный текст не найду, но могу примерно пересказать.
Там аналогия программирования со строительством. Приходит новый строитель на незаконченный объект, лаборатория на необитаемом острове, и видит то, что изображено на картинке сверху. Не поняв для чего нужны швабры, дирижабль и огромный вентилятор, он демонтирует все, и завершает проект, но после начала работы этой лаборатории случается утечка какого то газа. не зная почему это случилось и как устранить, он связывается с предыдущим строителем. Тот рассказывает, что швабры должны были поддерживать потолок, с верхнего этажа которого и упали бочки с химикатами, вентилятор должен был сдуть газ в случае выброса, а дирижабль для эвакуации в крайнем случае.
Вот так же при смене программиста в коде программы могут быть какие то непонятные части для нового программиста, убрав которые можно все испортить, но для чего они нужны тоже не понятно.
Не понимаю ненависти к goto. Крайне хороший метод - не надо писать многочисленные строки для выхода из циклов, им можно заменить кучу if-ов и множество другой шняги. К тому же, на процессорном уровне практически любой кейс/цикл является, как раз таки, несколькими операциями перехода. Если адекватно его применять, то он может повысить читабельность кода. Он крайне оптимален и удобен - так за что же его не любят?
2. Теряется линейность кода. Если это 1-2 перехода это терпимо, а вот больше превращаются в полный хаос и безобразие
Вообще по этому поводу достаточно литературы
По пункту 1 как раз приводят классический контрпример:
int result = initA();
if (!result) goto a;
result = initB_onlyAfterA();
if (!result) goto b;
// some logic
b:
freeB();
a:
freeA();
Это если язык вообще подразумевает чистку чего-то автоматически) Да, в какой-нибудь Java такую логику представить сложно. В C++ тоже можно сделать гораздо красивее. А вот на чистом C уже получаются неплохие оптимизации.
Сразу видно что ты системный код даже не читал, не говоря уж о написании. Даже не представляешь, как это что-то вдруг может не уметь само «почиститься».
someLogic(initA()||initB())
И опять зачистку выделенных ресурсов потерял.
Заодно представь, что A прошло успешно, а B обломалось.
>И опять зачистку выделенных ресурсов потерял.
Предположу что функция возвращает значение
>Заодно представь, что A прошло успешно, а B обломалось.
Для этого любая нормальная функция должна обрабатывать исключения внутри себя. Вообще не понимаю почему подобные вещи должны обсуждаться
>даже не читал, не говоря уж о написании.
Ну давай начнем грузить друг друга терминологией, заняться больше нечем чем какому-то рандомному анониму что-то доказывать.
При использовании goto ты можешь(Ну точнее это происходит неявно) запросто вызвать деструктор при выходе переменной из области видимости, перескочив через конструктор. Или, наоборот, не вызвать деструктор, успев при этом вызвать конструктор. В лучшем случае ты получишь утечку памяти и unexpected поведение, в худшем - просто ничего не заработает.
> Даже не представляешь, как это что-то вдруг может не уметь само «почиститься».
Ну, если у нас есть gc, то скорее всего мы просто можем забить на то что мы там где-то что-то объявляли/инициализировали. И то не стоит этого делать, дабы не получить постоянные блокирования потоков этим самым сборщиком. Но поскольку мы говорим о плюсах, любое невнимательное обращение с памятью или flow приведет к чему угодно, но только не к стабильной работе
Дружище, для справки: во множестве языков НЕТ никаких исключений и деструкторов :)) Вообще нет. Совсем.
И ещё для справки: C++ и C — разные языки. Причём первый довольно редко используется для системного кода, поскольку содержит слишком много undefined behavior и накладных расходов.
Но речь не только про C, отнюдь. Из современных ЯП это, например, ещё и Go. И да, оператор goto в нём есть.
"Исключение" это не только try/catch (Вот его терпеть не могу, засрут весь код им, читать невозможно) это в первую очередь обработка исключительных значений, asset, if(value) и т.п.
Что ты понимаешь под "системным кодом"? В первый раз слышу как определение чего-то
И почему мы вообще обсуждаем С? Это страшное легаси, его запретить надо, у него ub еще больше, а отсутсвие абстракций тупо раздувает код и возможность ошибок
И почему мы вообще обсуждаем С? Это страшное легаси, его запретить надо
Ггггг) Это твои JS-фреймворки (угадал?) становятся легаси через месяц. А вот альтернативы C на сегодняшний день тупо нет. Вообще нет. Совсем. И подвезти не обещали.
Почитай ответ Торвальдса на предложение переписать Git на C++. Занимательно, хотя и не везде цензурно) Кстати, обрати внимание на имя автора вопроса.
http://harmful.cat-v.org/software/c++/linus
Кстати, переносимость — тоже не BS, а ещё одна вещь, в которой у C конкуренты элементарно отсутствуют. Просто нет больше настолько переносимого кода, понимаешь?
Что ты понимаешь под "системным кодом"
Да вроде ничего нового. Код, критически важный для функционирования прикладного кода поверх него.
Ядра операционных систем, прошивки контроллеров, виртуальные машины, декодеры видеопотока, серьёзные математические библиотеки. Всё это — голый C, без исключений.
Причем, предположим, что ты понимаешь что делаешь, но достаточно по невнимательности/незнанию прописать
int result0 = initA();
if (!result) goto a;
int result1 = initB_onlyAfterA();
if (!result) goto b;
и превратить твой контрпример в пример того как делать нельзя; - об этом писал Голуб во всем известной книге про стреляние по ногам
Забавно, как раз Голуба достал из шкафа перечитать)
Беда всех таких «контрпримеров» в том, что невнимательность и опечатка остаются невнимательностью и опечаткой, а не дефектом самого подхода. Ты же можешь написать (Java):
class A {
String bar;
void foo(String baz) {
if (baz != null) doSomething(bar.length()); // NullPointerException
}
Беда ли Java в том, что ты перепутал bar и baz?)
Всеж давайте не путать опечатки с логическими ошибками, еще и завязанными на тонкостях конкретного языка
И я о том же! Не надо приводить чью-то опечатку как пример ошибки в языке.
Тем более что IDE уже лет двадцать на такое указывают.
Минусы минусами, но мне бы хотелось объяснения. Ибо все аргументы, которые я читаю сводятся к "не по канону", что сложно назвать минусом метода, имхо
это как пик ханзо в овере: не все на нем плохо играют, не такой уж это плохой герой, но как только ты его пикнул - сразу получил 5 репортов XD
Друг, играющий в овер, постоянно пикает Ханзо. В принципе, у него всегда были странные вкусы: ещё в школе он просил добавку гречки, а ещё брал макароны без котлеты... Всегда знала, что с ним что-то не так
Что не так с гречкой? А жрать котлеты из школьной столовки все равно что прыгать с крыши стройплощадки в снег
то-то я его как не пикну (ранг, быстрая или аркада - без разницы), то золото по урону мое (привет бастиону, хогу, риперу и крысе в тиме), золото по содействиям в убийствах мое (привет обезьяне, люсио и мойре в тиме) золото по убийству на объекте - мое (привет лоурэнж дамагерам) и зачастую даже золото по времени объекта (привет всем танкам и хилам). За игру стабильно минимум одна золотая медаль и 2 серебра, каждая вторая-третья катка - мой лучший момент, причем очень редко, когда заря, рейн или мей могут посапортить и законтролить врагов, чтобы скомбить мою ульту. Это не герой плохой, это тима из таких руинеров как ты, которые после пика ханзо трольпикают, фидят и дизморалят. и все это на ранге 2200.
Может Ханзо не играбелен на рейтингах 3500++, но до 2000 он гнет врагов как тетиву лука!
И да, ханзо - даже при этом не мой мейн, и аим у меня как у кота на джойстике. XD
С тех пор, как убрали из статистики урон по щитам, у них всё не так легко с голдой. Ну и я ж не аутист, чтобы на трёх названных тобой героях играть.
Очень много способов выстрелить себе в ногу. Слишком много. Можно забыть что-то удалить, очистить, закрыть. Можно попасть из готу на готу и обратно и зациклиться. Про повышение читаемости кода - это вообще какое-то странное утверждение. Если у меня 3 вложенных if? Это готу на готу на готу. Это туда-сюда по коду листать?
Процессорный уровень не нуждается в "понимании", проц просто выполняет команды. А когда ты читаешь код, то прыжки - это разрыв страницы, как сноска в книге, которую ты обязан прочитать. Листаешь до сноски, читаешь, возвращаешься, восстанавливаешь контекст, что был до сноски, и продолжаешь читать дальше.
Кроме того, ниже отметили, что возможны проблемы с чисткой. Например, выделенная на старте память может увеличиться под новые фишечки, а код после прыжка останется незамеченным, поэтому очистка в том сегменте останется старой. Пример грубый, но возможный.
В общем, использовать только по КРАЙНЕЙ необходимости и только если это лучшее решение. Компилятор и сам готов расставит, если вопрос в оптимизации.
На самом деле ассемблер кишит готушками, хотя на самом деле там conditional jump, т.е. то же самое что "если это то дуй туда". Но гото усложняет отладку и читабельность кода. Да и... да блядь... сука... хз... просто выйти красиво из цикла нужно, и не готошить из ебеня в другие ебеня, а вызвать метод, который внезапно тоже является готошкой, просто спрятанной в глубине машинного кода и с дополнительными финтифлюшками вроде передачи данных тем или иным образом.
Но гото усложняет отладку и читабельность кода.
С этого момента поподробней. Отрыл даже статейку на хабре
https://habrahabr.ru/post/114211/
Пример оттуда:
С goto
if (a)
{
A;
goto L3;
}
L1:
if (b)
{
L2:
B;
L3:
C;
goto L1;
}
else if (!c)
{
D;
goto L2;
}
E;
Без goto:
char bf1, bf2, bf3;
if (a)
{
A;
bf1 = 1;
}
else
bf1 = 0;
bf2 = 0;
do
{
do
{
if (bf3 || b)
bf3 = 1;
else
bf3 = 0;
if (bf3 || bf2)
B;
if (bf3 || bf1 || bf2)
{
C;
bf1 = 0;
bf2 = 1;
}
if (!bf3)
{
if (!c)
{
D;
bf3 = 1;
}
else
{
bf3 = 0;
bf2 = 0;
}
}
}
while (bf3);
}
while (bf2);
E;
Надо ли мне уточнять, какой из вариантов выглядит красивее и удобнее читается?
Да и... да блядь... сука... хз... просто выйти красиво из цикла нужно, и не готошить из ебеня в другие ебеня, а вызвать метод, который внезапно тоже является готошкой, просто спрятанной в глубине машинного кода и с дополнительными финтифлюшками вроде передачи данных тем или иным образом.
Порадовало. Достаточно вот этой вырезки:
просто выйти красиво из цикла нужно, и не готошить из ебеня в другие ебеня, а вызвать метод, который внезапно тоже является готошкой
То-есть опять всё сводится к "не по канону"?
Почему любое использование goto считается непрофессионализмом, если рабочий код с такой командой обозначает, что кодер действительно понимает ход работы своей программы?
у goto связь приходится специально отслеживать, повышается риск ошибки.
goto может быть и циклом и if'ом, придется специально разбираться, что есть что
программист, применивший goto, может понимать логику работу программы. а другой программист, пришедший на проект, может её не понимать, так что "по канону" это очень важно.
ни один из приведённых не выглядит красиво ни на пол рубля, ибо если нет табуляции, то делать такие столбы скобок - аморально
Да, каюсь. Выглядит ужасно, ибо я тупо скопипастил. Думаю, будет лучше посмотреть их в самом топике. Извиняюсь
Вы бы статейку промотали до комментариев.
Во первых это очень очень специфический и странный алгоритм
Во вторых его куски можно обернуть в функции и не использовать готу
В третьих там приведены варианты как это иожно без функйий и готу по челтвечески сделать
В четвертых хоть какой-то прирост перфоманса будет только на низкоуровневых языках типа си
В целом готу это штука, с которой очень легко обосраться, которая на больших участках кода становится плохочитабельной, плохотестируемой. при этом то удобство, которое оно дает, достаточно сомнительно и легко заменяется штатными средствами при минимальных издержках или вообще без оных
Ну и пацаны засмеют, это тоже немаловажно, когда ты работаешь в команде/опенсорс
По примеру - вот это перепрыгивание goto L3 после A непонятно. Мы ведь можем дублировать код (пусть даже функциями), можно просто продублировать выполнение C в случае истинности a. Прыгать в середину цикла это... Накуя?
UPD:
вообще статья неоднозначное впечатление производит. Сначала автор приводит пример, который по его словам
в жизни такие алгоритмы почти не встречаются
Затем приводит этот же алгоритм как довод к использованию goto
Затем на замечание в комментариях, что вместо ужасного блока с if'ами можно написать
https://s.mail.ru/Dsiw/r8a3RTZqU
говорит, что рефакторить нельзя, что вот такая задача, надо реализовывать вот это извращение.
имхо, если тебе запрещают это рефакторить, и заставляют реализовывать, можно и goto использовать, хуже уже не будет.
Вообще, вопросы к обоим фрагментам кода (в исходной статье): во втором хотя бы сразу видно что есть цикл. А первый, как отметили в комментариях, не корректен.
Если и использовать goto, то как и многопоточность, либо просто, либо никак, например, как выход из вложенных циклов, в другом случае инлайнить.
goto - как бензопила. В умелых руках - мощный и полезный инструмент (не верите - почитайте код ядра линукса), а в неумелых - пиздец всему, и неумелым рукам в первую очередь. Ну и продолжая аналогию, не в каждом проекте нужна бензопила, но есть такие где без нее не обойтись.
Пример, где нужен goto? За исключением выхода из нескольких циклов (для этого уже есть конструкции) и кодогенерации (где без него тоже прекрасно живется).
Я использую (на С) в случаях когда есть больше 2-3 аллокаций, открытых файлов, мьютексов, подключений к БД. Особенно мьютексов, с которыми часто нужно работать в строго определенном порядке (а иначе - дэдлок).
Чтобы не изобретать велосипед, приведу пример из ядра линукса:
kernel/power/hibernate.c
Каждый из goto пришлось бы заменить на всё, что написано под соответствующей меткой. Например вместо
goto Enable_irqs;
на 287 строке пришлось бы написать всё это:
local_irq_enable();
enable_nonboot_cpus();
platform_finish(platform_mode);
dpm_resume_start(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
return error;
на 287 строке пришлось бы написать всё это:
Нет, не пришлось бы. Здесь четкая вложенная конструкция.
Например, строчки 278-314 заменяются на
if (!error && !hib_test) {
__error = disable_nonboot_cpus()
__if (!error && !hib_test) {
__<все, что внутри>
__}
__enable_nonboot_cpus()
}
platform_finish(..);
И по мне так даже лучше.
с которыми нужно работать в строго определенном порядке (а иначе - дэдлок).
Можно пример (с goto)? Казалось бы, это ровно причина НЕ использовать goto.
Я все так заменю. Здесь я заменил 2, например.
Скиньте ссылку на код (или в pastbin пошлите). Я отредактирую, чтобы не осталось goto
скажи, ты (я на ты, ничего?) действительно хочешь в пятницу вечером сидеть и кодить, чтобы доказать свою точку зрения, потому, что в интернете кто-то неправ?
Peace, man!
Можно кодить с goto, а можно и без него. Большинству людей оно и не нужно. Но иногда приходится кстати. Мне, например, нравится, как написана эта функция, никакой путаницы нет.
У меня сейчас 16:38:)
Это заняло минут 5, лол. Из них 3 я проверял, что не накосячил.
тобы доказать свою точку зрения, потому, что в интернете кто-то неправ?
Эм. Всё началось с моей просьбы:
Пример, где нужен goto?Пока что я не видел примера, где нужен goto:)
Путаницы нет в частности потому, что ф-я очень прямолинейно отображается в ф-ю без goto. И в данном случае я предпочитаю вариант без него, чтобы не проверять вложенность goto и label-ов.
Без 5-6 уровней вложенных ифов, и с минимумом вызовов функций (это все таки код ядра)
вот вы свели тут все к программированию, а ебанутые теперь не могут понять чем оскорблены их чувства ))