Правда ли, что люди пишут безумный код с перекрывающимися побочными эффектами, сохраняя при этом невозмутимость?

Original author: Raymond Chen
  • Translation
Вашему вниманию предлагается перевод статьи Рэймонда Чена из блога The Old New Thing, посвященной проблемам кода, полагающегося на порядок вычисления выражений — и всем тем, кто пишет foo(i++, a[i]);
Порядок вычисления выражений определяется конкретной реализацией, за исключением случаев, когда язык гарантирует определенный порядок вычислений. Если же в дополнение к результату вычисление выражения вызывает изменения в среде выполнения, то говорят, что данное выражение имеет побочные эффекты.
MSDN
В нашей внутренней рассылке про C# регулярно возникает дискуссионный вопрос, который касается корректной интерпретации подобных конструкций:

a -= a *= a;
p[x++] = ++x;

В ответ я спрашиваю:
Да кто вообще пишет такой код с невозмутимым видом? Одно дело, когда такое пишешь, пытаясь победить в «Международном Конкурсе запутывания кода на Си» (IOCCC, International Obfuscated C Code Contest), или если хочешь написать головоломку — но в обоих случаях понимаешь, что ты занимаешься чем-то нестандартным. Что, реально есть кто-то, кто пишет a -= a *= a и p[x++] = ++x; и думает про себя «Чёрт возьми, да я пишу действительно классный код!»
На что Эрик Липперт отвечает мне: «Да, такие люди определенно встречаются». В качестве примера он привел одну успешную книгу популярного автора, который свято верил в то, что чем короче код, тем быстрее он работает. Так вот, представьте себе — продажи этой книги составляют уже свыше 4 миллионов копий и продолжают расти. Автор этой книги постарался впихнуть в каждое выражение несколько побочных эффектов сразу, плотно усеяв их условными тернарными операторами; всё дело в том, что он искренне верил в то, что скорость выполнения программы пропорциональна количеству использованных в ней точек с запятыми — и что каждый раз, когда программист объявляет новую переменную, Бог убивает щеночка.

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

Во-первых, будет большое количество ложных срабатываний. Допустим, вы напишете следующий код:

total_cost = p->base_price + p->calculate_tax();

Этот код вызовет предупреждение: компилятор увидит, что метод calculate_tax не является константным (const), поэтому он обеспокоится тем, что метод может изменить переменную base_price — и в этом случае иметь значение будет то, считаете ли вы налог по оригинальной base_price базовой цене, или по уже измененной. Теперь, допустим, что вы знаете (и эти знания компилятору недоступны), что метод подсчета налога calculate_tax обновляет значение локальной переменной налог (tax) в объекте, но не изменяет базовую цену; итак, для вас это предупреждение будет ложной тревогой.

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

Что ж, хорошо, сделаем шаг назад, и давайте будем предупреждать только о самых вопиющих случаях — только тогда, когда переменная изменяется и вычисляется в одном и том же выражении. "Предупреждение: Выражение полагается на порядок вычисления".

Возьмём супер-умного программиста Эксперта Джо: он знает, что его код безупречен, а компилятор — слабак. «Хорошо, да это же очевидно, что сначала делается инкремент переменной, затем она используется для вычисления индекса массива, а затем результат поиска в массиве сохраняется обратно в переменную. Здесь нет конфликта порядка вычисления. Тупой компилятор!». В результате супер-умный программист Эксперт Джо отключит это предупреждение, сочтя его бесполезным. Что ж, наш Эксперт Джо — это всё-таки безнадежный случай, и за него мы не беспокоимся.

Но возьмем другого программиста, Новичка Джо — на деле, он даже не поймёт сути этого предупреждения. «Ну ок, давайте посмотрим. Я компилировал эту функцию пять раз, и каждый раз я получал одинаковый результат. Результат выглядит для меня надежным. Похоже на то, что предупреждение было ложным». Таким образом, как раз те, кто должен был получить пользу от этого предупреждения, не всегда обладает достаточными знаниями, чтобы понять его.

Конечно же, проходит некоторое время, и этот вопрос всплывает в рассылке вновь. Кто-нибудь обязательно спросит, почему выражение x ^= y ^= x ^= y не работает в C#, хотя работает в С++. Вот вам ещё одно доказательство того, что некоторые всё-таки пишут код, который полагается на несколько побочных эффектов сразу — и эти же люди искренне считают, что их код очевиден и гарантировано будет работать.

Ссылка на оригинал
Ссылка на обсуждение
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 277

    +3
    Жуть конечно, но это в порядке вещей… Я когда заказывал сайт на фрилансе такое тоже встречал)
      +39

      Для меня правило хорошего тона всегда брать в скобки и выносить на отдельную строку/в отдельную переменную то, что вызывает повышенное шевеление извилинами в во всём остальном обычном коде. Не вижу никакого смысла постоянно напрягать память для того, чтобы вспомнить каким именно образом компилятор или интерпретатор обрабатывает тот или иной уникальный случай. Проще написать больше тупого кода, чем меньше излишне умного. Сложно придумать аргументацию против такого подхода.


      Хотя в последнее время читая код весьма продвинутых программистов вижу на удивление большое количество без необходимости сокращенных названий переменных, невыразительных названий функций/методов и полное отсутствие малейшего комментирования происходящего. Может это я такой тупой, но всё же не вижу никакого смысла в сокращении privateKey до pk и прочих подобных, ибо код превращается в кроссворд из кучи переменных каждая из которых содержит максимум 3 символа, а чаще вообще 1.

        +1
        >а чаще вообще 1.
        Но-но-но! Имена некоторых однобуквенных переменных уже стали нарицательными. Попробуйте обозначить координату не через x,y,z или счётчик не через i,j,k и вы увидите много непонимающих взглядов:)
          +5

          Я имел ввиду гораздо менее понятные сокращения) Против указанных в общем случае не имею ничего против.

            +5
            я работаю сейчас с географическими/прямоугольными координатами. Там х и у наоборот. И часть библиотек написано математиками(х вправо, у вверх), а часть геодезистами(наоборот). Боюсь, мне захочется назвать их сложнее :))
            А z там есть, но спрятан.
              +1
              О как это знакомо! Вероятно это будет fi и la (φ и λ) или lat и lon.
                0
                Вероятно это будет fi и la (φ и λ) или lat и lon.
                А почему не φ и λ?
                  +5
                  Можно и φ и λ, если живёте где-нибудь в Афинах и у вас на клавиатуре есть эти символы. Правда я думаю, что это будет равносильно написанию названий переменных на русском у нас.
                    +5
                    Если мы говорим о мейнстриме (C++/C#/Java), то общая практика состоит в избегании применения не ASCII символов в коде. До сих пор бывают проблемы с кодировкой исходников, что приводит к нежелательным последствиям. А ещё можно представить, что потом эти названия полезут в некое публичное API. А это накладывает дополнительное требование поддержки не ASCII-символов на все инструменты, которые с этим API работают, будь это линкеры для библиотек или OData/REST кодогенераторы для WebApi. Слишком большая проблема для ровного места :)
                    +5

                    phi и lambda?

                  +8
                  Забавный пример из личной практики, связанный с именами переменных — когда в конце 80-х я осваивал программирование на МК-61 и МК-52, то одной из моих любимых книжек была книжка небольшого формата Очкова и Хмелюка «От микрокалькулятора к персональному компьютеру». Там были примеры программ как для микрокалькуляторов, так и на Бейсике. Меня в то время долго мучал вопрос — почему во многих программах фигурирует переменная с загадочным именем TEMP, никак не связанная с температурой ))) (cитуация усугублялась тем, что в школе я учил немецкий).
                    0
                    temporär (нем.)
                      +1

                      zeitweilig куда как более популярное прилагательное в немецком.

                    –6
                    i, j и k это зло. В циклах должны быть нормальные переменные типа rowIndex, columnIndex и т.п. Два вложенных фора c i/j очень часто на этапе написания содержат ошибку. И даже если ошибки нет, время потраченное на осмысление можно использовать более продуктивно.
                      +6
                      Два вложенных фора c i/j очень часто на этапе написания содержат ошибку.

                      Прокладку, между стулом и клавиатурой (=
                      Значение этих переменных очевидно настолько, что замена мнемониками пользы не принесет. Есть такое правило: чем меньше скоуп жизни переменной, тем короче имена. Если в таком цикле возможно даже i с j перепутать, то тут уже стоит подумать о том, как его отрефакторить.
                        +5

                        На самом деле, стоит задуматься о шрифтах в любимом редакторе (или IDE).

                          +1
                          А я согласен с TerraV.

                          Я делаю имена итераторов и счётчиков цикла короткими, но мнемоническими. Например, итераторы/счётчики для addresses, users, rows, columns будут называться a, u, r, c, а не a, b, c, d (и не i, j, k, l). Если одной буквы не хватает для уникальной идентификации (например, columns и cells, или tests и testTargets) — двумя или даже полными словами.
                            +4
                            Тут действительно большая разница между i,j,k и хитромудрыми a,b,c,d, о семантике которых любой бы задумался. for и i — это как хлеб и рама.
                              0
                              Вы, наверно, имели в виду «хитромудрыми a, u, r, c».
                                +4
                                Их тоже, это не имеет значения на самом деле) Если в контексте имя переменной не дает стойкой ассоциации (как, например, r в геометрической формуле), то лучше воздержаться.

                                Я делаю имена итераторов и счётчиков цикла короткими, но мнемоническими. Например, итераторы/счётчики для addresses, users, rows, columns будут называться a, u, r, c

                                Использование i,j,k в качестве индексаторов — это общепринятая практика, а однобуквенные штуки намекающие на тип будут уместнее в foreach-циклах, где это не просто индекс, а экземпляр, обладающий состоянием и поведением.
                                  –2
                                  Экземпляр, обладающий состоянием и поведением? Похоже на унижение школьника учителем.
                                    +2
                                    Нет, это не пикабу, здесь сидят специалисты, владеющие терминологией.
                                      –2
                                      Этой терминологии всего несколько десятков лет, она вполне может поменяться. Меня же коробит что используют неочевидные языковые конструкции, наталкивающие на неправильные мысли. Я бы написал так: это не просто индекс, а элемент структуры данных. Статья именно об этом — о неочевидности кода.
                                        +3
                                        Фундаментальные понятия имеют интересное свойство: меняться либо редко, либо никогда.

                                        Меня же коробит что используют неочевидные языковые конструкции, наталкивающие на неправильные мысли

                                        Любая наука обрастает своей профессиональной терминологией, что поделать?.. Или вы предлагаете «хреновинами» и «фиговинами» оперировать? (=
                                        Если для вас это неочевидно, то это печальненько, ибо рассказывают это студентам на первом курсе.

                                        Я бы написал так: это не просто индекс, а элемент структуры данных.

                                        Сложными словами я как раз подчеркивал, что индекс != элементу коллекции, это всего лишь подярковый номер, без «состояния и поведения».
                              0

                              А если это шаблонный метод?

                                +1
                                И? В чём разница?

                                А, я понял. Вы вспомнили про ту глупую традицию называть параметры шаблонов T, T1, T2, …? Ну так это тоже нафиг.

                                Параметры шаблонов должны иметь осмысленные имена. Стилизания их названий должна быть такая же, как и стилизация того, что они обозначают (если параметр-тип и типы мы пишем с большой буквы, то с большой; если параметр-константа и константы мы пишем так-то, то так-то). MySuperCollection<Item> (или на худой конец MySuperCollection<ItemType> — хотя это спорно, мы ведь называем Integer и CustomerInfo, а не IntegerType и CustomerInfoType — ну да ладно), но не MySuperCollection<T>.
                                  0
                                  И? В чём разница?

                                  Есть некая someFunction<TInput, TOutput>, где в качестве TInput и TOutput может быть ну совершенно что угодно. Какие буквы вместо i и j вы предложите для переменной-индекса?

                                    –1
                                    i и o — очевидно же.
                                +2
                                А вот этого я не понимаю. Ладно i/j/k означают не более чем «число от 0 до x», но зачем сокращать итераторы?

                                Другое дело — функции, делающие что-то тривиальное, когда кроме «v1», «v2» сложно выдумать что-то подходящее. «firstValue/secondValue»?
                                  –1
                                  А в чём принципиальная разница между целочисленными счётчиками цикла и итераторами — не понимаю.
                                    +1
                                    Огромная. В том, что здесь мы имеем дело не с абстрактным «числом от одного до десяти», а с конкретным элементом конкретного списка. И логично элемент списка «addresses» назвать «address», «users» — «user» и т.д.
                                      0
                                      Итератор — это, как правило, объект, позволяющий получить доступ к ячейке контейнера. Например, итератор позволяет вам перемещаться между узлами связного списка, где инкремент счетчика бесполезен.
                                        +1

                                        Дело в том, что целочисленные индексы используются всегда совместно с коллекцией: users[i]. При этом вся необходимая для понимания семантики информация уже сосредоточена в имени коллекции.


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

                                  +3
                                  Зло, нужно for-ы по возможности заменять foreach-ами.
                                    +4
                                    Зря минусите человека.

                                    i,j,k — вполне применимы в простых случаях без вложенных циклов, хотя и не помню когда последний раз их использовал, т.к. легко заменяются на foreach.
                                    А вот при работе с таблицами я бы предпочёл видеть rowIdx и colIdx — чуть больше писать, зато позволяет избегать детских, но труднонаходимых ошибок в коде.
                                  –2
                                  Не так давно разбирался в одноразовой функции getAndConvertPhysicalToLogicalValue и нихрена не понял откуда она и что берёт. В результате функция вырезана нафиг, а логика работы (то, что функция должна была делать) перенесена в тело цикла. При этом пропала передача параметров и прочие сопутствующие причандалы.

                                  Где-то есть золотая середина.
                                    +17
                                    нихрена не понял откуда она и что берёт
                                    Т.е. функция была сложна и непонятна?
                                    логика работы (то, что функция должна была делать) перенесена в тело цикла
                                    Т.е. тело цикла и функции, в котором он находится, стало ещё более сложным и непонятным? А в чём профит Вашего действия?
                                      0
                                      Функция была странная, а термины «Logical» и «Physical» в этом контексте никогда не употреблялись.
                                      Тело цикла стало больше, но в силу того, что параметры никуда не передаются можно отследить логику работы.
                                        +10

                                        Переименовать не пробовали?)

                                      +3
                                      getAndConvertPhysicalToLogicalValue

                                      Вот такой фигни не должно быть, функция должна выполнять одно действие. И это безотносительно того, что непонятно, что за значения такие.
                                        +1
                                        Согласен на 100%.
                                        Функция вызывалась один раз, называлась длинно с использованием непонятных терминов, передавала всякое туда-сюда по значению.
                                        Заменилась на три строки в теле цикла.
                                          +1

                                          Если бритва Оккама позволяет отсечь эту функцию, то вполне норм от нее избавиться, считаю.


                                          Имеет смысл запаковывать логику в методы, когда это действительно имеет смысл (вот такая тавтология) — то есть, если там предполагаются изменения, либо она относится к какой-либо отличной зоне ответственности, нежели вызывающий метод, либо имеет место DRY, либо еще что.


                                          Если там дествительно три строки, да и еще название не соответствует, возможно, там когда-то раньше было больше, а потом разработчик это убрал, да поленился остатки перенести назад или переименовать нормально. Избавление от старого хлама есть часть рефакторинга.


                                          Ну а золотая середина (имхо) в том, чтобы придерживаться четких принципов и методологий ака SOLID, GRASP и прочих, которые как раз помогают четко детерминировать границы когда "так" делать, и когда "так" не делать.

                                      +4

                                      Комментарии в чужом коде — большая моя боль. Бывает, пытаешься понять что-то в какой-то OpenSource библиотеке, открываешь какой-нибудь исходный файл и не видишь ни единого комментария, кроме лицензии, и хорошо, если это 500 строк, а не 5000. И так постоянно. Зачем они так делают?


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

                                        +6

                                        Зависит от того, как ещё переменные и методы обозваны. При хорошему подбору названий необходимость в комментариях резко уменьшается.

                                          +1
                                          Главный критерий достаточности количества комментариев — чтобы сторонний человек не реагировал как Alex_ME (собственно я так же реагирую)
                                            +3
                                            Вот сейчас это был очень популистский камент :)
                                            +2

                                            Проблема в том, что иногда не понятно, что писать в методе. Он часто говорит сам за себя, если вы находитесь в контескте логики приложения.

                                              +2
                                              Это он в момент когда ты его пишешь говорит сам за себя. Через полгода или просто если его другой человек откроет, уже будет сложно понять и контекст и смысл метода.

                                              Я работал с программистом, который делал метод, к примеру, «FindMemo», и оставлял комментарий: «Файндит мемо». Несмотря на то, что добавить ему было нечего, понятней этот кусок кода не становился.
                                                +1

                                                Так о том и речь — что добавление комментария не добавляет смысла. Если метод назван понятно — то и комментарий не нужен. Если метод назван непонятно — то и комментарий будет таким же непонятным :-)

                                                  +4
                                                  Дак я не о том =)

                                                  Опять же, если взять мой пример с «FindMemo», рядом есть дефолтный метод Find. Для чего нужно было делать кастомный, и что такое memo в текущем контексте можно узнать только если детально разобраться в методе. Эту информацию и нужно было оставить в комментарии
                                                    +1

                                                    Вот только вместо нормального комментария был оставлен комментарий "Файндит мемо". Почему? Потому что программист думал что это понятно.


                                                    Чтобы написать правильный комментарий, нужно было чтобы программист осознал что "Файндит мемо" — непонятно. Но в таком случае что помешало бы ему и метод тоже назвать по-другому?

                                                      0

                                                      Обычно надо не метод переименовывать, а подробней описать аргументы и результат (ну, как обычно описаны функции у ms/apple/..., и в doxygen/… удобно делать)

                                                        0
                                                        То, что в этом случае метод имел бы имя длиной символов в 50. Для комментария-то это нормально…
                                                          +1
                                                          И для функции — тоже нормально, если короче понятное название не придумывается. Важно же, чтобы в том месте, где функция вызывается было понятно что она делает — а там вашего чудного комментария уже и не будет.
                                                            +1
                                                            На помощь приходит ide, которая в подсказке указывает описание метода и параметров
                                                              –1
                                                              Вы читаете код, тыкая в каждую функцию и читая описание метода и параметров? Мне вас жаль.

                                                              На практике код приходится читать гораздо чаще, чем писать, так что важно оптимизировать именно скорость чтения кода.
                                                              0
                                                              Сталкивался со случаями, когда мне говорили: «Непонятное название — укороти.»
                                                              Категорически не согласен с таким — слишком длинное (и написанное на грамотном английском) название может быть неудобным в использовании или ещё что-то, но не непонятным.
                                                              Жаль, что существующие языки/IDE не предлагают вменяемых механизмов сокращения названий функций (типа, вообще она называется длинно и в достаточно удалённых модулях её будут называть полностью, но в нашем модуле, имеющем с этим набором функций дело часто, мы будем называть их так-то, так-то и так-то, а не полностью).
                                                                0
                                                                use function cos as c;
                                                                echo c(0);  // 1
                                                                
                                                                var getById = document.getElementById;
                                                                console.log(getById);  // function getElementById() { [native code] }
                                                                0
                                                                Надо смотреть по каждому конкретному случаю. Если имя функции такое длинное, оно не будет влазить с параметрами в строку.
                                                                0
                                                                Методы не висят в воздухе, а лежат внутри типов (инстансы которых также могут иметь имена), реализующих интерфейсы, которые находятся в пространствах имен. Всё это уточняет контекст.
                                                                А длинные имена (едва ли там три слова дают в сумме 50 символов) говорят о том, что метод скорее всего нарушает SRP. (Было бы неплохо посмотреть на реальный пример)
                                                                  +1
                                                                  Ок, пусть это не метод, а просто функция.

                                                                  А длинные имена (едва ли там три слова дают в сумме 50 символов) говорят о том, что метод скорее всего нарушает SRP.

                                                                  Допустим, некая функция извлекает элемент определенным образом, так что просто ExtractItem недостаточно внятное название.
                                                                    0
                                                                    Имена свободных функций обычно длиннее. Ну ОК, это не сильно меняет суть.

                                                                    некая функция извлекает элемент определенным образом, так что просто ExtractItem недостаточно внятное название.

                                                                    Нужен какой-то хороший пример. Что за айтем, откуда мы его берем? Если добавится еще 2-3 слова, то ничего страшного в этом нет. В названии нужно описывать цели, а не алгоритмы.
                                                                • UFO just landed and posted this here
                                                                    +2
                                                                    … и будет нифига непонятно, как они работают в целом) Надо все-таки соблюдать баланс.
                                                                    • UFO just landed and posted this here
                                                                        0

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

                                                                      +1
                                                                      Есть такая штука: разбиение метода на два и более.
                                                                      Если вам хочется назвать метод более чем 3-мя словами, то либо у вас словесный понос, либо ваш метод делает слишком много одновременно и нарушает хотя бы принцип единственной ответственности (single responsibility).

                                                                      Не в этом дело.

                                                                      Это редкие случаи, но вполне реальные. Я сейчас не буду искать по коду эти редкие случаи, вот синтетический пример: у вас есть метод, извлекающий из коллекции элемент по некоему идентификатору целочисленного типа. И еще один метод, извлекающий элемент по целочисленному идентификатору, но другому.
                                                                      Соответственно, у ва сбудет метод ExtractItemByID (например), и, условно, ExtractItemByOriginalID

                                                                      Если разбивать методы дроблением до совершенно малых, то теряется логика работы, как хорошо их ни назови. Более того, придется называть наоборот, более многословно, чтобы пояснить, что же именно вот этот абстрактный оторванный от жизни кусок делает; тогда как в норме это была бы часть другого, более крупного метода и не нуждалась бы в пояснениях ни через комментарии, ни через название в силу наличия контекста.
                                                                        0
                                                                        Можно выбирать идентификатор через замыкание. Можно передавать параметр, который будет выбирать идентификатор. Можно придумать множество других способов.
                                                                        • UFO just landed and posted this here
                                                                            0
                                                                            Если разбивать методы дроблением до совершенно малых, то теряется логика работы, как хорошо их ни назови.

                                                                            Не теряется, если разбивать по SRP.

                                                                              –1

                                                                              Тоже думаю, что теряется, так как в пределе получается ассемблер.

                                                                                0

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

                                                                                  –1

                                                                                  Я не про оптимизацию, а про структуру в целом. Много мелких операций.

                                                                              0
                                                                              Соответственно, у ва сбудет метод ExtractItemByID (например), и, условно, ExtractItemByOriginalID

                                                                              Что с ними не так?
                                                                    +1

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


                                                                    Или вот, например, бывают такие методы.Возможно не очень понятно, что он делает сам по себе, но если вы находитесь в контексте работы приложения (а именно, это бот для работы с чатами), то в целом становится понятно.


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

                                                                      +1
                                                                      Через полгода или просто если его другой человек откроет, уже будет сложно понять и контекст и смысл метода.
                                                                      Агрумент про полгода, пожалуйста, уберите. Я его слышу уже больше 10 лет, но открываю свой код 10-летней давности… и эффекта не наступает. Да, конечно, мне приходится немного почитать свой собственный код, чтобы «вьехать» в то, что он делает — ну так и комментарии мне пришлось бы читать, какая разница?
                                                                        0

                                                                        Зависит от опыта программирования.
                                                                        Первое время (учёба в университете) я открывал код полугодовой свежести и ничего не понимал.
                                                                        Сейчас уже насобачился писать код, который легко понять даже через несколько лет.

                                                                        • UFO just landed and posted this here
                                                                      0

                                                                      Иногда люди не очень дружат с английским и использование становится странным и непонятным

                                                                      • UFO just landed and posted this here
                                                                          0

                                                                          Вы же понимаете, что код надо уже вчера, а английский минимум через неделю? А писать особо и некому, и чувак, по-случаю, вполне неплохо общается с ООП, алгоритмами и прочим, но вот английский дальше "a boy ate an apple" не зашел, но готов доблестно бороться с Google Translate.

                                                                          • UFO just landed and posted this here
                                                                            0
                                                                            Из вашего высказывания получается, что код писать могут только англоговорящие люди. И другим нет места в программировании? По-моему довольно спорное утверждение?
                                                                              +2
                                                                              Ну смотрите: и так очевидно, что без английского сейчас сложно устроиться в нормальную контору, нельзя почитать доку на официальном сайте библиотеки и прочее. Но в конкретном примере человек даже не может читать и писать понятный код, то есть делает свою работу плохо.
                                                                                +2
                                                                                И другим нет места в программировании?
                                                                                Им скоро не будет места почти нигде. Пока ещё не так, но к этому всё движется. Мир интегрируется — хорошо это или плохо. Причём в программировании это происходит чуть быстрее ввиду совершенно естественных причин. Не умеешь читать/писать/разговаривать по-английски — считай, не умеешь читать/писать/разговаривать вообще. Конечно, людям с объективными ограничениями (болезнь, возраст и т.п.) стоит сделать скидку, я не спорю. Конечно, теоретически ситуация может поменяться (деглобализация, другой мировой язык или ещё что-то). Но пока выглядит как-то так.
                                                                                  +1
                                                                                  Конечно английский учить не нужно. Пишите на 1С там же русский. Это конечно сарказм.

                                                                                  В современном мире знать свой язык плюс английский это просто самый необходимый минимум для специалиста в любой отрасли.
                                                                                  А в идеали нужно выучить еще хотябы два языка.
                                                                                    0
                                                                                    Извините, если высказался слишком категорично.
                                                                                    • UFO just landed and posted this here
                                                                                        0
                                                                                        Извиняюсь, подумал и понял, что был неправ. Фактически, мной (и, подозреваю, другими тоже — но не уверен) руководило во время написания этого комментария «всяк кулик своё болото хвалит». То есть у каждого человека есть какой-то свой набор умений (и неумений), и ему хочется верить, что именно его умения — самые важные (а его неумения — некритические). Это из серии «не служил в армии — не мужик», «каждый должен владеть компьютером» и «неумение водить машину в современном обществе равносильно инвалидности».

                                                                                        На самом деле никто не знает, какой именно набор умений наиболее важен (или оптимален при каком-то складе характера). Уж, по крайней мере, не уже-владеющему навыком об этом судить (а вот кого-то, кто бы говорил: «я не умею то-то и из-за этого пострадал там-то» — можно было было послушать). Допускаю, что существуют паттерны жизни, не завязанные на английский, просто я в них не разбираюсь.
                                                                                  0

                                                                                  +1 к комментарию SirEdvin, и стандартное пожелание: шлите патчи. Во многих open source проектах рук не хватает.

                                                                                    +2
                                                                                    Скорее всего, Вы просто сначала думаете, что должен сделать данный программный объект, начерно проектируете его структуру и только потом приступаете к наполнению кодом. К сожалению, правило «Сначала пойми, что ты хочешь написать и только потом начинай кодировать» нынче не сильно в чести…
                                                                                      0
                                                                                      Как раз если сначала думать и проектировать структуру, то методы, классы и прочие логические единицы выходят самодокументирующимися.
                                                                                        +1
                                                                                        Но на смеси английского языка и языка формальной логики. А так иногда хочется понять, что-же хотел сделать автор — ведь учителя английского у нас, скорее всего были, разные :-)
                                                                                      +9
                                                                                      открываешь какой-нибудь исходный файл и не видишь ни единого комментария, кроме лицензии, и хорошо, если это 500 строк, а не 5000. И так постоянно. Зачем они так делают?
                                                                                      Не зачем, а «почему».

                                                                                      Мы с одним моим хорошим знакомым по этому поводу регулярно спорим. Для меня программа — это описание решения задачи, но она, в первую очередь, написана на C++ (Java, C#, PHP — нужное подчеркнуть). А комментарии — это налоги «сносок в тексте», поясняющих непонятные или странные моменты, которые никак не удаётся выразить на языке программирования.

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

                                                                                      Для моего же знакомого комментарии — это основное, текст программы — это, так, «что-то такое для компилятора», а описание программы должно быть на «человеческом» языке. И он начинает «кипятком писать», когда в ответ на вопрос «а что сюда, собственно, передавать» он получает ответ «ну из кода же это очевидно!».

                                                                                      Почему среди OpenSource преобладают люди первого вида, а «за деньги» — в основном работают люди второго вида я не знаю…
                                                                                        +1
                                                                                        комментарии — это налоги «сносок в тексте», поясняющих непонятные или странные моменты, которые никак не удаётся выразить на языке программирования.
                                                                                        Иногда бывает, что красивый, стройный, понятный алгоритм… тупит. Выполняется не приемлемое количество времени. И вот тогда начинается черная магия с рекурсией, вложенными циклами с next/prev, временными переменными, etc. Как вы считаете — надо оставить понятно и медленно, или странно и быстро — но с несколькими строчками комментариев, объясняющих «магию» (и предупреждение «ничего не трогай!» :) )
                                                                                          +4
                                                                                          Зависит от того, критичен ли для вас этот код и можете ли вы себе оставить медленную версию.

                                                                                          И да — это как раз такое место где в книге вы можете обнаружить врезку (или сноску) на две страницы, обьясняющую что здесь происходит (возможно с отсылками на другие работы и прочее).

                                                                                          Я не против длинных комментариев, более того — мой рекорд это описание строчек примерно в 50 к фукции из одной строчки (с отсылками на места в разных версиях C и C++ стандартов, обьясняющих почему этот код не просто «случайно здесь работает», а будет работать на всех реализациях, совместимых со стандартом).

                                                                                          Но сам принцип — «комментарий == сноска с пояснением в книге» для меня по прежнему является основным…
                                                                                            0
                                                                                            Если код тупит, то скорее всего, это не из-за читаемости кода, а из-за того, что по другому не захотели делать. И от «рефакторинга» добавлением комментариев в духе "// тут рекурсия и вложенный цикл" лучше не станет.
                                                                                              0

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

                                                                                            • UFO just landed and posted this here
                                                                                                +3
                                                                                                Они просто приходят к 9 и уходят в 6.

                                                                                                Вы так говорите, будто это что-то плохое.
                                                                                                • UFO just landed and posted this here
                                                                                                    +1
                                                                                                    Я все чаще сталкиваюсь с мнением, что быть доступным для работы 24/7 — это признак успеха, причем, что самое милое, у наёмных работников. В то время, когда в подавляющем большинстве компаний творится всякий scrum и все подчинино расписанию, то подобное кивание на часы выглядит странно. Не смог продавить свой чудо-рефакторинг на планировании? Так это ты ССЗБ стахановец, а не те, кто отрабатывает приоритетные таски в заложенные часы.
                                                                                                    • UFO just landed and posted this here
                                                                                                        0
                                                                                                        не фиксить их проактивно, ждать пожара… В ответ надо выдать эстимейт достаточный для хорошего рефакторинга

                                                                                                        Хитрая тактика (= Хотя, если вы не зарылись в «пожарах», вокруг все не так уж плохо.

                                                                                                        А о менталитете, в котором пребывание на рабочем месте важнее, чем выполненная работа и ее качество.

                                                                                                        Мне кажется, что проблема здесь начинается с начальства, которое не волнует качество, а нужно видеть озабоченные лица за компьютерами с 9 до 18. В гос.конторах и других крупных бюрократических организациях такое практикуется.
                                                                                                        • UFO just landed and posted this here
                                                                                                            0
                                                                                                            Пустые офисы? Врятли… если работа настолько проста… просто подкинет ещё в топку, и так до того уровня на котором работники будут едва справляться и постоянно загружены. Но тут возникнет другая проблема — люди в таких условиях быстро выгорят и перестанут работать вообще.
                                                                                              0
                                                                                              У ЧакНориса есть такое выражение — «комментарии плохо пахнут».
                                                                                              Т.е. надо писать код так, чтоб комментарии вообще не требовались… Ибо часто бывает, что поменяв код или скопировав — комментарий остается без изменения и может обманывать программиста. Плюс такого подхода — названия переменных и функций должны быть гипер-продуманными и понятными. Ну и кода без комметариев на мониторе больше видно.

                                                                                              Видимо, в open source кадый мнит себя ЧакНорисом :)
                                                                                                +2
                                                                                                Ибо часто бывает, что поменяв код или скопировав — комментарий остается без изменения и может обманывать программиста.
                                                                                                Если комментарий противоречит коду, то есть проблема, да.
                                                                                                Иногда проблема в том, что код не соответствует замыслу программиста с самого начала.

                                                                                                А если комментария нет, то проблема не видна: код соответствует тому, что он делает, а не тому, что он должен делать.
                                                                                                  +6
                                                                                                  Иногда проблема в том, что код не соответствует замыслу программиста с самого начала
                                                                                                  Есть такая шутка: код делает то, что написал программист, а не то, что он хотел написать :)
                                                                                                    0
                                                                                                    Есть такая шутка: код делает то, что написал программист, а не то, что он хотел написать :)
                                                                                                    В десятку. Код всегда соотвествует программе. Он может не соответствовать замыслу, он может не соответствовать каким-то великим идеям, но он всегда верно и точно описывает то, что делает программа на самом деле.

                                                                                                    А чтобы работать с программой мне, в общем-то, это и нужно. Какое мне, собственно, дело, до тех идей, которые роились в голове у человека, когда он это писал? Да никакого! Мне важно как оно здесь и сейчас работает!
                                                                                                      +1
                                                                                                      Так речь о том, что код может соответствовать замыслу в одной реализации компилятора, но не соответсвовать в другой
                                                                                                        0
                                                                                                        Ну такой код хоть где-то хоть чему-то но соотвествует. Комментарий вообще может не иметь отношения к тому, что программа делает.
                                                                                                        0
                                                                                                        Да никакого! Мне важно как оно здесь и сейчас работает!

                                                                                                        А почему вы по умолчанию считаете, что оно работает? :-)

                                                                                                          0
                                                                                                          Оно всегда работает. Даже если автор породил код путём соединения рандомных кусков со stackoverflow руководствуясь принципом Пусть будет, как будет — ведь как-нибудь да будет! Никогда так не было, чтобы никак не было.

                                                                                                          Другое дело, что оно может не делать того, что нам нужно — но почему вы считаете, что человек не способный написать работающий код сможет написать при этом толковый комментарий?
                                                                                                            0

                                                                                                            Потому что я видел такие комментарии.

                                                                                                              0

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

                                                                                                                0
                                                                                                                Могу поверить что подобное иногда происходит, но в моей практике гораздо чаще бывает так, что код работает (пусть и не совсем так, как его автор предполагал), а комментарий — неверен.

                                                                                                                Возможно где-нибудь в аэрокосмической отрасли написание программы дважды (один раз в комментариях, второй — «перевод» на язык программирования) имеет смысл, но в большинстве случае IMNSHO это излишне.
                                                                                                                  0

                                                                                                                  Зачем вы все время говорите про дублирование? Комментарии не должны дублировать реализацию.


                                                                                                                  // прямое обращение, потому что работает быстрее
                                                                                                                  x->a = b;
                                                                                                                  // хак, чтобы сработал сеттер, так как ...
                                                                                                                  x->a = b;

                                                                                                                  В зависимости от языка и описания x цели одной строчки могут быть совершенно разные.

                                                                                                      +1

                                                                                                      Ну обманывает, прям проблема. Заметил, что не соответствует коду — возьми и поправь. При кардинальном изменении логики и комментарии удаляются. А при небольшом в них все равно есть полезная информация. Даже бывает, что становится понятна логика написавшего, и где ошибка. Конечно это если комментарий по делу. Бесполезные комментарии, дублирующие код, не нужны.

                                                                                                        +1
                                                                                                        Ну обманывает, прям проблема.
                                                                                                        Да, таки проблема.

                                                                                                        Заметил, что не соответствует коду — возьми и поправь.
                                                                                                        Этого невозможно заметить, если вы не читаете код, а читаете только комментарии. А если вы читаете код и понимаете его настолько, что можете исправить и комментарий — то зачем вам там комментарий вообще? Он только к лишней трате времени приведёт.

                                                                                                        Бесполезные комментарии, дублирующие код, не нужны.
                                                                                                        Тем не менее я видел кучу стайл гайдов, которые требуют обязательно описывать все функции в поноценном doxygen-стиле. В результате имеем кучу комментариев тупо дублирующих код.
                                                                                                          +2
                                                                                                          Этого невозможно заметить, если вы не читаете код, а читаете только комментарии.

                                                                                                          А кто сказал, что надо читать только комментарии?


                                                                                                          то зачем вам там комментарий вообще?

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


                                                                                                          обязательно описывать все функции

                                                                                                          Это документация, а не комментарии, дублирующие код. Внутренняя реализация может измениться, но если внешнее поведение не изменилось, то и документация не изменится. И наоборот, документация к функции может дополняться без изменения кода. Документация — это не просто комментарии, для нее иногда даже особый формат комментирования делают.

                                                                                                            +1
                                                                                                            А кто сказал, что надо читать только комментарии?
                                                                                                            Никто не сказал. Но обычно люди, жалующихся на острый недостаток комментариев, как выясняется, код читать не хотят вообще. Или, по крайней мере, читают его когда что-то непонятно из комментария. Что, как мне кажется, извращает саму идею довольно сильно: комментарий должен прояснять код, а не код — являться разъяснением спорных моментов в комментарии!

                                                                                                            Это документация, а не комментарии, дублирующие код.
                                                                                                            В 90% случаев функции делают что-то относительно несложное (что именно — описано в названии, параметры описывают что на входе и что на выходе) и это именно что дублирование даже не кода, а заголовка функции. В случае, когда функция делает какое-то нетривиальное действие комментарии, разумеется, уместны — но таких функций, обычно, немного.
                                                                                                              +1
                                                                                                              Люди обычно жалуются на недостаток комментариев в непонятных местах, а не вообще по коду.
                                                                                                                +2
                                                                                                                Почитайте топикстартера: Бывает, пытаешься понять что-то в какой-то OpenSource библиотеке, открываешь какой-нибудь исходный файл и не видишь ни единого комментария, кроме лицензии, и хорошо, если это 500 строк, а не 5000.

                                                                                                                Жалоба была именно на то, что нет комментариев «вообще», а не на то, что какое-то сложное место не описано.
                                                                                                                  0
                                                                                                                  Бывает, пытаешься понять

                                                                                                                  Комментарии в таком количестве кода это некоторые точки, в которых можно быть уверенным, что понял правильно, и от них уже отталкиваться.
                                                                                                                    +2
                                                                                                                    Комментарии этого не помогают понять. Для этого совсем другие документы нужны. Описание архитектуры или что-то подобное — совершенно отдельно от куда и описывающие всё в общих чертах. Иногда в начале кода можно подобное найти в виде отдельного большого комментария — но это не единственный, и зачастую не лучший способ.

                                                                                                                    Ориентироваться же по комментариям в коде — это всё равно что пытаться понять что есть в книге не по оглавлению, а по сноскам.
                                                                                                        +1

                                                                                                        Пример комментария к месту:


                                                                                                        https://github.com/python/cpython/blob/master/Python/pyhash.c#L34

                                                                                                          0
                                                                                                          Очень хороший пример. Отдельное описание структуры из которого понятно — что делает весь остальной код. Вместо кучи странных замечаний из которых получается ребуйс…
                                                                                                        +2
                                                                                                        Самая большая моя боль была при первом столкновении с библиотекой, завязанной на glib. Куча функций вообще без единого коммента. Ладно, внутри функций, бог с ним. Но хотя бы о самой функции — что за аргументы, что она возвращает? Нет, зачем, и так же все понятно.

                                                                                                        Т.к. многие функции возвращали указатели, постоянно приходилось угадывать, нужно ли потом память по этим указателям освобождать самостоятельно? Или нужно вызывать g_object_unref? Или можно вообще ничего не делать?

                                                                                                        Ох и намаялся я, пока все утечки памяти выискивал.
                                                                                                          0

                                                                                                          Вот потому-то и надо умные указатели использовать...

                                                                                                            0
                                                                                                            В библиотеке на C (не C++)? Как вы это себе представляете?
                                                                                                              +1

                                                                                                              Отказаться уже от Си. Это была шутка.

                                                                                                              +1
                                                                                                              Так это и были умные указатели. Только на С. Поэтому ref/unref нужно вызывать руками.
                                                                                                              0
                                                                                                              Т.к. многие функции возвращали указатели, постоянно приходилось угадывать, нужно ли потом память по этим указателям освобождать самостоятельно? Или нужно вызывать g_object_unref? Или можно вообще ничего не делать?
                                                                                                              А документацию прочитать — не судьба?
                                                                                                                +1
                                                                                                                А как документация для glib поможет мне понять вот эту функцию из библиотеки, которая от glib зависит?
                                                                                                                const char *
                                                                                                                arv_device_get_string_feature_value (ArvDevice *device, const char *feature)


                                                                                                                Она возвращает указатель на строку. Эту строку нужно освобождать? Или не нужно? Комментариев к функции или внутри функции нет вообще. Строка прямо в ней не формируется, она берется из другой функции.

                                                                                                                В итоге из-за отсутствия комментария (банально шапки к функции), приходится не просто читать код этой конкретной функции, а прослеживать весь путь, который эта несчастная строка проходит, прежде чем до моего вызова доберется.
                                                                                                                  +1
                                                                                                                  В таких случаях нужен метакомментарий на модуль (а то и на весть проект), который бы описывал — как это узнать из названия функции. Обычно какой-то стиль является «основным», а функции, которые ему не следуют — помечены особо.
                                                                                                                    0
                                                                                                                    Комментарий для модуля был весьма абстрактный — «Тут описан такой-то класс, он делает вот это». Какого-то единого метакомментария для всего проекта я не обнаружил. Возможно, конечно, я плохо искал…

                                                                                                                    Судя по git blame комментарии-шапки добавлялись для какого-то генератора биндингов, видимо, некомментированные функции этому генератору были не нужны.
                                                                                                                +2

                                                                                                                Хех… типичный пример API функции у BLAS:
                                                                                                                csymm (SIDE, UPLO, M, N, ALPHA, A, LDA, B, LDB, BETA, C, LDC)
                                                                                                                Нужно больше боли ;-)

                                                                                                                –1
                                                                                                                Модные best practices настаивают, что комменты это плохо и все функции\переменные должны быть самодокументируемыми. Поэтому современные каргокультисты от программирования считают коментарии моветоном и максимально их избегают.
                                                                                                                +2

                                                                                                                Я Вам более того скажу — молодые программисты на C++ написанием такого кода просто бравируют — "я могу понять как этого будет работать/вычисляться, а моему тимлиду надо пойти освежить свои знания или поломать голову чтобы разобраться".

                                                                                                                  +1
                                                                                                                  Для себя вывел правило. Если видишь на собеседовании такой код — однозначно попал в молодой и дружный коллектив.
                                                                                                                  +6
                                                                                                                  А я pk для себя расшифровываю как primaryKey (при работе с БД)
                                                                                                                    0
                                                                                                                    Дело в том что когда программист плотно работает с конкретными терминами и сокращениями для него они становятся очевидными, а длинные имена часто используемых переменных — раздражающе длинными.
                                                                                                                      +2

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


                                                                                                                      Ещё бесят сокращения вроде privKey (в последнее время работал с кодом для цифровых подписей). Почему не написать privateKey, всё равно ведь IDE дописывает слова сама? Короче, вижу много сомнительных решений и делаю выводы для себя чтобы писать более приятный для чтения код:)

                                                                                                                        +1
                                                                                                                        Когда требуется делать сокращения (например, полное имя идентификатора состоит из слов так 5-6), пишу пояснение в том месте, где происходит объявление. Чтобы человек, читающий код, понимал мою «логику сокращений».
                                                                                                                        0
                                                                                                                        В чем проблема делать псевдонимы с ссылками на полную версию. Тогда для опытных участников — код всегда будет лаконичным, а для стажеров — понятным.
                                                                                                                          0
                                                                                                                          И поддерживать потом обе версии?
                                                                                                                      0
                                                                                                                      Самое хреновое, когда писатели такого кода начинают учить (или заставлять) других как надо писать.
                                                                                                                      Комментарии конечно вещь полезная, но тут всё зависит от ситуации.
                                                                                                                        +6
                                                                                                                        В качестве примера он привел одну успешную книгу популярного автора, который свято верил в то, что чем короче код, тем быстрее он работает.

                                                                                                                        Кто-нить знает что это за книга? Стало интересно почитать.

                                                                                                                          0
                                                                                                                          Скорее всего, специально не указывают, чтобы не получить какие-то иски. Эрик Липперт подтверждает существование этой книги тут. Можете попробовать спросить у него.
                                                                                                                          –2
                                                                                                                          можно научить компилятор анализировать такой код и выдавать предупреждение

                                                                                                                          На уровне компилятора любая двусмысленность должна порождать либо Warning, либо ошибку компиляции по причине undefined behaviour. Странно, что разработчики компилятора об этом не заботятся, а потом приходится юзать всякие PVS Studio и пр.
                                                                                                                          Интересно бы услышать мнение Andrey2008

                                                                                                                            –3

                                                                                                                            Я думаю, это ближе к особенностям языка. Зачем то же такие выражения как ++i и i++ были нужны…

                                                                                                                              –4
                                                                                                                              Обычая перезакладка. Недаром в Go оставили только i++ и не как операцию, а как инструкцию.
                                                                                                                                +1

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

                                                                                                                                  +1
                                                                                                                                  Это надо смотреть на ассемблеры некоторых ВМ тех лет.
                                                                                                                                  +12

                                                                                                                                  UB — это не ошибка компилятора, а невыполнение программистом контракта.


                                                                                                                                  Например, компилятор быстро выполняет сложение (не проверяет потенциальное переполнение), но программист должен гарантировать, что если у него знаковые числа и переполнение возможно — он его не допустит.


                                                                                                                                  В таком случае компилятор делает сложение с помощью одной инструкции, а не 5, но налагает определенный контракт на разработчика. Если программист не выполнил соответствующий контракт — то поведение не определено (собственно, UB), компилятор имеет право сожрать ваши ботинки.

                                                                                                                                    +1

                                                                                                                                    > компилятор имеет право сожрать ваши ботинки.
                                                                                                                                    Скорее, компилятор имеет право сгенерить код, который сожрёт ваши ботинки.

                                                                                                                                      +1

                                                                                                                                      Вот и неплохо было бы Warning получать, чтобы не держать в голове все детали "контракта".

                                                                                                                                        +1

                                                                                                                                        Будет слишком много ложноположительных срабатываний и warning просто будет после этого отключен.


                                                                                                                                        В том же rust пошли немого другим путём и в debug-сборке, например, overflow при сложении знаковых целых приведёт к панике (исключению), но debug-сборка там обычно работает в разы медленнее. В release-сборке поведение при этом, как минимум, implementation specific, хотя, скорее всего, будет близко к UB из Си. А кому нужна именно сумма с переполнением напишет overflowing_add явно.

                                                                                                                                          0
                                                                                                                                          Вопрос из интереса: а можно ли rust-компилятору сказать, что integer overflow сделан умышленно и ошибки здесь нет?
                                                                                                                                            +1

                                                                                                                                            https://doc.rust-lang.org/std/primitive.i32.html, см. checked_add, saturating_add, wrapping_add и overflowing_add. Выбираете нужный явно и никто потом не гадает что имелось ввиду и есть ли какие-нибудь особые куски контракта не отраженные в коде.

                                                                                                                                              +2
                                                                                                                                              Решение — тривиально до невозможности. В gcc/clang тоже добавили — вот только непонятно почему на это потребовалось чуть не полвека мучений…
                                                                                                                                                0

                                                                                                                                                Оно формально intrinsics?


                                                                                                                                                А мучения, видимо, чтоб жизнь мёдом не казалась. И в стандарт доедут году к двадцатому..

                                                                                                                                                  0
                                                                                                                                                  Оно формально intrinsics?
                                                                                                                                                  Угу. Код достаточно оптимальный порождается, семантика тоже определена.

                                                                                                                                                  И в стандарт доедут году к двадцатому..
                                                                                                                                                  Экий вы оптимист, батенька…
                                                                                                                                                    0
                                                                                                                                                    Экий вы оптимист, батенька…

                                                                                                                                                    Я просто работая с поделиями по развитию застрявшими в конце девяностых немного забыл, что уже 17 год)) Так что исправляюсь и скажу, что ждем и надеемся на C37 ,)

                                                                                                                                                    –1
                                                                                                                                                    Надо сказать, что я С++ всеми силами избегаю. Лучше уж сразу на ASM, способов прострелить ногу меньше.
                                                                                                                                                      +1

                                                                                                                                                      Писать игры на ASM такое себе удовольствие. Особенно, когда используешь какой-нибудь UnrealEngine или еще какой-нибудь движок.

                                                                                                                                                        0
                                                                                                                                                        Вот именно поэтому я и не пишу игры.
                                                                                                                                                        А вообще это был сарказм.
                                                                                                                                              0
                                                                                                                                              В release-сборке поведение при этом, как минимум, implementation specific, хотя, скорее всего, будет близко к UB из Си

                                                                                                                                              В релизе поведение вполне чётко определено (насколько это может быть для языка у которого нет стандарта).

                                                                                                                                                0

                                                                                                                                                Это неотличимо от implementation defined, т. к. существует только один компилятор, да.

                                                                                                                                                  0

                                                                                                                                                  Ну в "reference" языка данное требование прописано, но я затрудняюсь судить какой статус будет у реализации (частично) игнорирующей такие требования.

                                                                                                                                              0
                                                                                                                                              Это весьма непросто сделать во время компиляции, так как потенциальное нарушение контракта не является проблемой если программа его реально не вызывает.
                                                                                                                                              0

                                                                                                                                              Мне кажется, компилятор или должен генерировать максимально близкий машинный код, передавая ответственность за поведение процессору (и выдавая предупреждение об этом), или не должен его вообще компилировать. А не трогать чужие ботинки.

                                                                                                                                                +1

                                                                                                                                                Вы готовы пожертвовать 80-90% производительности для десктопа? Получить десятикратное увеличение стоимости услуг и программ? Если да, то есть простые компиляторы с простой кодогенерацией, но в конкурентной гонке general purpose софта побеждают отнюдь не надёжные и медленные программы, которые имеют 5-10% нужного функционала. Пока нет специальных требований на надёжность при допустимой низкой производительности (как в, например, hard realtime), никто этого делать не будет.


                                                                                                                                                При этом есть большая ниша прикладного софта, где нет проблем с производительностью. И пишут её на java/c#/python, где проблемы UB не стоит, т. к. managed окружение с примерно одной виртуальной машиной (на каждую из платформ) за счёт TCK и подобных решений. В случае питона это не совсем так, но близко.

                                                                                                                                                  –1

                                                                                                                                                  Откуда такие цифры? Хотите сказать, что 80-90% производительности для десктопа достигается за счет кода с UB?) Я предполагаю, что большинство кода написано без UB, следовательно, компилятор там ничего убирать не будет.

                                                                                                                                                    +1
                                                                                                                                                    Я предполагаю, что большинство кода написано без UB

                                                                                                                                                    Без, но с учётом же. Таким образом у компилятора есть возможность проводить некоторые свои оптимизации.
                                                                                                                                                      +3
                                                                                                                                                      Я предполагаю, что большинство кода написано без UB, следовательно, компилятор там ничего убирать не будет.
                                                                                                                                                      Вы это серьёзно?

                                                                                                                                                      Пример кода, который потенциально может вызвать UB: a = b + c;. Или ещё так: free(p);. Или, на худой конец: i++.

                                                                                                                                                      Много вы программ видели, которые ничего этого не содержат? Или где такого кода очень мало? Я видел — это обычно 100500 обёрток, которые и без всякого UB тормозят так, что им никакой компилятор не поможет…
                                                                                                                                                        0
                                                                                                                                                        a = b + c
                                                                                                                                                        Тем не менее, компилятор генерирует для этого выражения что-то вроде add eax, ebx или аналогичного ему по поведению. А не запускает форматирование жесткого диска. Это я и имел в виду под «генерировать максимально близкий машинный код».
                                                                                                                                                          0
                                                                                                                                                          Вы неправы. Он вполне может и не сгенерировать, если посчитает, что так быстрее, а результат будет тот же самый (кроме результатов при UB)
                                                                                                                                                            +1
                                                                                                                                                            Тем не менее, компилятор генерирует для этого выражения что-то вроде add eax, ebx или аналогичного ему по поведению.
                                                                                                                                                            Простейший компилятор — да, может быть. Да и отптимизирующий тоже, если это выражение присутствует в отдельной функциии и ничего другого там нет — но кому такая функция нужна?

                                                                                                                                                            А вот если «чего другого» там есть, то оптимизирующий можен много чего со всем этим сделать. Может засунуть константу прямо в оператор обращения к памяти, например. Или перенести её куда-то. Да много чего можно сделать если знать, что переполнения не будет. А его не будет, так как программист обещал!
                                                                                                                                                              0
                                                                                                                                                              И как ни странно, это тоже «максимально близкий машинный код», потому что дает тот результат, который требуется — в a будет сумма b и c. Даже если a появляется только во внутренних регистрах процессора при вычислении сложной адресации.
                                                                                                                                                                +1
                                                                                                                                                                И как ни странно, это тоже «максимально близкий машинный код», потому что дает тот результат, который требуется — в a будет сумма b и c.
                                                                                                                                                                Не будет в a суммы b и c. Потому что оно туды не влезет. В результате у вас индекс, который, вообще говоря, планировался быть short'ом после оптимизаций окажется равным 70000 — и вы будете материть компилятор на чём свет стоит.

                                                                                                                                                                Даже если a появляется только во внутренних регистрах процессора при вычислении сложной адресации.
                                                                                                                                                                В том-то и дело, что нет — там появляется a только в том случае если программист позаботился об этом и написал программу так, что она не вызывает UB.

                                                                                                                                                                А дальше, если за этим не следить, получается «снежный ком» — тут у нас оказалось 70'000 в short'е, там — мы полезли не в тот обьект, вынули не то, засунули не туда… и вот уже ваша программа самоуничтожается. До форматирования винчестера дело [пока?] не дошло — но всё ещё впереди!
                                                                                                                                                                  0

                                                                                                                                                                  Если после оптимизаций получилась сумма 70000 при входе 2 и 2, значит это неправильные оптимизации. Если же программист сознательно складывает 60000 и 10000 в short, то он не будет материть компилятор, когда не получит 70000. Потому что это понятное и логичное поведение. Тем более, что компилятор его предупреждал. Поэтому непонятно, откуда у вас получился снежный ком.


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

                                                                                                                                                                  Вот я и говорю, так быть не должно. Написано, значит должно появляться, или не компилироваться. А не модифицироваться молча с другим поведением в результате модификации.

                                                                                                                                                                    +1

                                                                                                                                                                    А где грань? Почему в случае a = b + c предполагается доверять программисту что тот ничего не забыл — а в случае, условно, a = b->c; надо программиста предупреждать?

                                                                                                                                                                      –2

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

                                                                                                                                                                      +2
                                                                                                                                                                      Если же программист сознательно складывает 60000 и 10000 в short, то он не будет материть компилятор, когда не получит 70000.
                                                                                                                                                                      В том-то и дело, что может получить в результате оптимизаций.

                                                                                                                                                                      Тем более, что компилятор его предупреждал.
                                                                                                                                                                      Он его на каждую операцию сложения предупреждал? И программист этого не отключил? Это какой-то неправильный программист и он явно занимается неправильным делом. Ему мандалы из песка делать нужно.

                                                                                                                                                                      Поэтому непонятно, откуда у вас получился снежный ком.
                                                                                                                                                                      Снежный ком получается когда у вас одна «невозможность» цепляется за другую «невозможность». Ниже я пример разобрал.

                                                                                                                                                                      Написано, значит должно появляться, или не компилироваться.
                                                                                                                                                                      Есть маленькая проблеммка: узнать — вызывает программа UB или нет, в общем случае, невозможно. Проблема остановки, мать её. Потому компилятор исходит из того, что программист — сам себе не враг, UB не допускает и из этого исходит.

                                                                                                                                                                      О самых вопиющих случаях компиляторы сообщают.
                                                                                                                                                                        –1
                                                                                                                                                                        В том-то и дело, что может получить в результате оптимизаций.

                                                                                                                                                                        Мы же про 16-битный short? Откуда там будет 70000, если для него надо 17 бит?


                                                                                                                                                                        Он его на каждую операцию сложения предупреждал?

                                                                                                                                                                        Я не особо специалист в C++, наверно чего-то не понимаю. То есть, сейчас в программах любая операция сложения это UB, с которой компилятор может сделать все что угодно?


                                                                                                                                                                        вызывает программа UB или нет, в общем случае, невозможно

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

                                                                                                                                                                          +1
                                                                                                                                                                          Оптимизирующий компилятор может увидеть после операции сложения операцию вычитания, деления или ещё какую, и поставить её раньше, если решит, что так нужно для оптимизации. А может и не поставить.
                                                                                                                                                                            +1
                                                                                                                                                                            Мы же про 16-битный short? Откуда там будет 70000, если для него надо 17 бит?
                                                                                                                                                                            На многих процессорах нужно предпринимать специальные усилия, чтобы «обрезать» число (x86 — редкое исключение, а не правило) и компилятор, зная о том, что переполнений не бывает вполне может производить вычисления с большей точностью. В x86 так тоже может быть — но в довольно специфических условиях.

                                                                                                                                                                            То есть, сейчас в программах любая операция сложения это UB, с которой компилятор может сделать все что угодно?
                                                                                                                                                                            Любая операция сложения потенциально может приводить к UB, если результат «не влезет» в соответствующий тип. Компилятор, в общем, не так часто может понять — будет результат «влазить» или нет. Так что «с консервативным подходом» ему придётся выдавать предупреждения на каждую операцию сложения, про которую он ничего не сможет доказать. То есть про большинство из них.
                                                                                                                                                                              +2
                                                                                                                                                                              Вы так говорите, словно в компиляторах есть строчка «if (isUndefinedBehavior) system(»format C:\");".
                                                                                                                                                                              в стандарте языка есть вещи, которые компилятор обязан отслеживать. И они это делают. Соответственно, все оптимизации производятся с учетом этих требований. А неопределенное поведение потому и неопределенное, потому что даже разработчик компилятора не скажет, во что оно выльется.
                                                                                                                                                                                –1

                                                                                                                                                                                Так разработчик компилятора и не должен ничего говорить. Неопределенное значит неопределенное, пусть процессор и ОС разбираются. Не надо его выбрасывать или заменять на другое, как в примере с циклом. Даже там в комментах шутят про "rm -Rf /". Просто по-моему это не то, что должен позволять стандарт языка.

                                                                                                                                                                                  +2
                                                                                                                                                                                  Если вам не нравится существование UB — пишите на языках, в которых нет UB. Хотите писать на C — смиритесь. Или напишите компилятор, который вас устроит. Только никому, кроме вас он не будет нужен, потому, что потеряется главное преимущество C — скорость выполнения.
                                                                                                                                                                                    –3

                                                                                                                                                                                    Причем здесь "смиритесь"? Это обсуждение причин и возможностей, а не pull request в стандарт. Поговорить на эту тему теперь тоже нельзя?

                                                                                                                                                                                      +2
                                                                                                                                                                                      Поговорить-то можно, но проявлять воинствующее невежество не стоит.
                                                                                                                                                                                        –3

                                                                                                                                                                                        Я высказываю аргументы в защиту своей точки зрения. Если я где-то рассуждаю неправильно, укажите где и приведите свои аргументы. Где именно здесь невежество, да еще и воинствующее?

                                                                                                                                                                                          +2
                                                                                                                                                                                          В неприятии вами того факта, что эффективность оптимизаций, вызывающих UB и не вызывающих может отличаться на порядок.
                                                                                                                                                                                            0
                                                                                                                                                                                            Строго говоря ему ещё не удалось привести пример оптимизации, которая не приводила бы к неработоспособности чьй-нибудь программы. Я, впрочем, пример такой оптимизации привести могу. Если у вас в программе написано два раза "mov eax, ebx" (вот прямо подряд, без зазоров), Их вроде бы можно заменить на один mov. Я не знаю — как это «заметить». Хотя, может быть, и тут можно, просто у меня недостаточно богатое воображение.

                                                                                                                                                                                            Но много вы на таких оптимизациях получите? А просто заменить «лишний» mov, как было нашим горе-воякой предложено — нельзя.

                                                                                                                                                                                            P.S. Языки без UB — это языки не дающие использовать несколько потоков и на дающие возможность напрямую манипулировать памятью. Иначе — никак. Даже в Go и Java — полно UB, потому что манипулировать миром, на который кто-то может смотреть «сбоку» и что-то там изменить так, чтобы этого не стало заметно — практически невозможно.
                                                                                                                                                                                              0
                                                                                                                                                                                              Если у вас в программе написано два раза "mov eax, ebx" (вот прямо подряд, без зазоров), Их вроде бы можно заменить на один mov.

                                                                                                                                                                                              При чуть более сложном варианте вида mov eax, [esi] два раза уже, по сути, заменить два movа на один нельзя даже в однопоточном коде при запрещенных начисто прерываниях. Вдруг по адресу из esi лежит mmio-регион.

                                                                                                                                                                                                –3
                                                                                                                                                                                                А просто заменить «лишний» mov, как было нашим горе-воякой предложено — нельзя.

                                                                                                                                                                                                Это был просто пример, вырванный из контекста. Выбрасывание этой инструкции аналогично выбрасыванию любого другого кода. Чем это принципиально отличается от оптимизации в вашем примере?


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

                                                                                                                                                                                                Покажите пожалуйста, где я утверждал, что оптимизация должна гарантировать работу стороннего кода, считающего такты или что-то подобное? Я привел пример с разворачиванием цикла, это вполне нормальная оптимизация. Чем это вас не устраивает?
                                                                                                                                                                                                А в вашем примере оптимизация на основе UB привела к неправильному выполнению программы.


                                                                                                                                                                                                Языки без UB — это языки не дающие использовать несколько потоков и на дающие возможность напрямую манипулировать памятью.

                                                                                                                                                                                                Я не пытаюсь предложить язык, результат компиляции которого будет полностью без UB, и нигде такого не утверждал. Я считаю, что компилятор наоборот не должен полагаться на UB и должен оставлять его как есть.

                                                                                                                                                                                                  0
                                                                                                                                                                                                  Дайте ссылку на коммент, где вы привели пример с разворачиванием цикла? Или вы имеете ввиду «повторить цикл 8 раз, значит надо делать его 8 раз»? Как раз здесь вполне можно оптимизировать вплоть до того, что ничего не повторять ни одного раза, если результат (не считая случаев с UB) от этого не изменится.
                                                                                                                                                                                                    0

                                                                                                                                                                                                    Да. "Можно 8 раз скопировать действия без цикла и выбросить сравнение i".


                                                                                                                                                                                                    Как раз здесь вполне можно оптимизировать вплоть до того, что ничего не повторять

                                                                                                                                                                                                    В том примере ничего не повторять не получится, так как заполняются значения по указателю. А в целом да, если в цикле одно действие x = 2;, то цикл можно убрать, так как результат не изменится. Здесь нет никакого противоречия моим словам. "значит надо делать его 8 раз" было сказано в контексте того примера, так как там поведение цикла распространяется за пределы цикла и функции.

                                                                                                                                                                                                    +2
                                                                                                                                                                                                    Чем это принципиально отличается от оптимизации в вашем примере?
                                                                                                                                                                                                    Ничем — и в этом-то всё и дело.

                                                                                                                                                                                                    Покажите пожалуйста, где я утверждал, что оптимизация должна гарантировать работу стороннего кода, считающего такты или что-то подобное?
                                                                                                                                                                                                    Дык эта:
                                                                                                                                                                                                    Я считаю, что компилятор наоборот не должен полагаться на UB и должен оставлять его как есть.
                                                                                                                                                                                                    Вот прямо-таки туточки.

                                                                                                                                                                                                    Вы либо трусы наденьте, либо крестик снимите. Либо у вас оптимизатор имеет право поломать программу, которая вызывает UB («считает такты или что-то подобное»), либо нет.

                                                                                                                                                                                                    Потому что пока ваши хотелки выглядят так: копилятор имеет право делать для оптимизации что угодно — но не должен ломать моих программ… чужие — можно.

                                                                                                                                                                                                    Так не бывает, уж извините. Нужен полный и точный перечень того, чего «хорошая», «правильная» программа делать не должна (в частности, по вашему — не должна считать такты). И вот этот список — и есть список UB, на которые полагается ваш компилятор.

                                                                                                                                                                                                    Компилятор, который оставляет все UB как есть — не может оптимизировать, фактически, ничего и никак…
                                                                                                                                                                                                      –1
                                                                                                                                                                                                      Ничем — и в этом-то всё и дело.

                                                                                                                                                                                                      Почему вы тогда сказали, что "просто заменить нельзя", если компиляторы проводят такие оптимизации? Раз ничем не отличается, значит можно.


                                                                                                                                                                                                      Вот прямо-таки туточки.

                                                                                                                                                                                                      Не вижу связи. Не могли бы вы прямо по пунктам написать логические выводы, приводящие к противоречию? Мне правда интересно, возможно я что-то не так понимаю.
                                                                                                                                                                                                      Оптимизации бывают не только из-за UB. И не каждую ситуацию из тех, которые в стандарте C++ называются UB, нельзя оптимизировать. Я ниже привел примеры под спойлером, что я имею в виду под "не должен полагаться на UB".


                                                                                                                                                                                                      чужие — можно
                                                                                                                                                                                                      в частности, по вашему — не должна считать такты

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


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

                                                                                                                                                                                                      Оптимизатор должен обеспечивать то же поведение. Если до оптимизации было 2 граничных случая, то и после нее должно быть столько же. А не a = 42;


                                                                                                                                                                                                      не может оптимизировать, фактически, ничего и никак…

                                                                                                                                                                                                      Я же привел примеры. Развернуть цикл можно? Можно.

                                                                                                                                                                                                        +2
                                                                                                                                                                                                        Оптимизации бывают не только из-за UB.
                                                                                                                                                                                                        Оптимизатор полагается на то, что некоторые действия являются UB. И, соотвественно, в программе не встречаются.

                                                                                                                                                                                                        Почему вы тогда сказали, что «просто заменить нельзя», если компиляторы проводят такие оптимизации?
                                                                                                                                                                                                        Соблюдая ваши «правила игры» (компилятор наоборот не должен полагаться на UB и должен оставлять его как есть) — нельзя. По правилам игры, в которую «играют» разработчики компиляторов — конечно можно!

                                                                                                                                                                                                        Я наоборот говорю, что не должно быть требований, что программа что-то должна. Считает — ее дело, пусть программист учитывает, что может быть оптимизация.
                                                                                                                                                                                                        Круто. У вас буквально две соседние фразы противоречат друг другу! Я впервые вижу такое проявления двоемыслия в споре.

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

                                                                                                                                                                                                        Оптимизатор должен обеспечивать то же поведение. Если до оптимизации было 2 граничных случая, то и после нее должно быть столько же. А не a = 42;
                                                                                                                                                                                                        Пример вашей «хорошей» оптимизации это требование нарушает. До оптимизации выход из цикла был возможен, после — нет.

                                                                                                                                                                                                        Я же привел примеры. Развернуть цикл можно? Можно.
                                                                                                                                                                                                        В общем случае — нельзя.

                                                                                                                                                                                                        Рассмотрим практический пример:
                                                                                                                                                                                                        uint64_t Read64A(const uint8_t* src) {
                                                                                                                                                                                                          uint64_t result = src[0];
                                                                                                                                                                                                          for (int i=1;i<8;i++)
                                                                                                                                                                                                            result |= (uint64_t)src[i] << (i * 8);
                                                                                                                                                                                                          return result;
                                                                                                                                                                                                        }

                                                                                                                                                                                                        Развёрнутый (и затем свёрнутый) цикл:
                                                                                                                                                                                                        Read64A(unsigned char const*):
                                                                                                                                                                                                                mov     rax, qword ptr [rdi]
                                                                                                                                                                                                                ret
                                                                                                                                                                                                        

                                                                                                                                                                                                        Казалось бы — великолепная оптимизация, слава компилятору!

                                                                                                                                                                                                        Одна беда — если теперь вы в эту память будете из другого потока писать либо 0x0000000000000000, либо 0xffffffffffffffff, то прочитать оттуда 0x00000000ffffffff вы не сможете ни за что и никогда. А оригинальный код это сделать мог. И это вполне могло помогать кому-то программу.

                                                                                                                                                                                                        P.S. Только не надо про то, что «стало только лучше» и «скорее всего программист этого и хотел». Эти критерии — неформализуемы и их, в общем, невозможно использовать для того, чтобы решать — является ли что-то хорошей оптимизацией или нет.
                                                                                                                                                                                                          +3
                                                                                                                                                                                                          Оптимизации бывают не только из-за UB

                                                                                                                                                                                                          Не из-за а благодаря.
                                                                                                                                                                                                          Если до оптимизации было 2 граничных случая, то и после нее должно быть столько же.

                                                                                                                                                                                                          А откуда известно, какие «2 граничных случая» были до оптимизации? Учтите, что C — кроссплатформенный язык, и сколько там «граничных случаев» будет на той или иной платформе — неизвестно.
                                                                                                                                                                                                          Развернуть цикл можно? Можно.

                                                                                                                                                                                                          Какая разница, сегодня или месяц назад, главное собаку-то я покормил!
                                                                                                                                                                                                          P.S. А цикл развернуть без предположений о возможности UB тоже нельзя, и вам уже несколько раз показали почему.
                                                                                                                                                                                                            +1
                                                                                                                                                                                                            P.S. А цикл развернуть без предположений о возможности UB тоже нельзя, и вам уже несколько раз показали почему.
                                                                                                                                                                                                            О невозможности UB. Невозможности, которую должен обеспечить программист.

                                                                                                                                                                                                            Любая оптимизация опирается на тот факт, что программу у нас «хорошая», чего-то «плохого» не делает. Вот если это «плохое» в принципе «железо» позволяет сделать — это и есть UB.

                                                                                                                                                                                                            Понятно, что тут всегда встаёт вопрос компромиса: что именно мы можем заставить разработчика не делать, а чего не можем. То есть вопрос отнесения чего-то к UB — это вопрос обсуждаемый. Что-то, что стандарт называет UB, конкретный компилятор может и не называть UB, а, наоборот, допускать что в программе такое происходит. А может иметь свои UB (обычно считается, что это «дефект, который когда-нибудь пофиксят», но это когда-нибудь может растянуться на долгие годы). Но если уже что-то отнесено к UB — то, разумеется его в программе быть не должно и компилятор вправе на это полагаться.

                                                                                                                                                                                                            Выдача же при этом полезных предупреждений — отдельная и весьма сложная задача.
                                                                                                                                                                                                              –3
                                                                                                                                                                                                              А откуда известно, какие «2 граничных случая» были до оптимизации? Учтите, что C — кроссплатформенный язык, и сколько там «граничных случаев» будет на той или иной платформе — неизвестно.

                                                                                                                                                                                                              Из исходного кода. Если в исходном коде присваивание было только при условии, то после оптимизации не должно присваиваться всегда. Тем более что в правильно написанном коде переменная должна быть инициализирована, и такой оптимизации не будет. PVS-Studio ведь как-то находит такие ошибки, несмотря на то, что C — кроссплатформенный язык.


                                                                                                                                                                                                              А цикл развернуть без предположений о возможности UB тоже нельзя, и вам уже несколько раз показали почему.

                                                                                                                                                                                                              Вы упорно не хотите понять, что я пытаюсь объяснить. Случаи, которые можно назвать UB, бывают разные. Если коротко, то что делает PVS-Studio, должен делать компилятор.

                                                                                                                                                                                                                +1
                                                                                                                                                                                                                Тем более что в правильно написанном коде переменная должна быть инициализирована, и такой оптимизации не будет.
                                                                                                                                                                                                                Но если человек её не проинициализировал, то это значит что ему всё равно что оттуда вернётся — так почему бы и не 42? Кода меньше, а в правильно написанной программе — это «стрелять» не должно.

                                                                                                                                                                                                                PVS-Studio ведь как-то находит такие ошибки, несмотря на то, что C — кроссплатформенный язык.
                                                                                                                                                                                                                PVS-Studio — это отдельный продукт, специально «заточенный» под то, чтобы отлавливать ошибки в программах. А компилятор — это компилятор. Компилятор решает одну задачу: сделать так, чтобы программа не вызывающая при своём исполнении UB работала быстро и требовала мало памяти. Всё остальное — не к компилятору. Не надо превращать компилятор в Die Eierlegende Wollmilchsau, пожалуйста, это приведёт только к тому, что все задачи будут решаться одинаково плохо.
                                                                                                                                                                                                                  0
                                                                                                                                                                                                                  Тот же «исходный код» может быть библиотекой, которая используется в 100500 разных проектах. И та или иная часть её может никогда не понадобиться. И без соответствующих оптимизаций придётся либо при каждом вызове проверять кучу условий, которые всегда не выполняются, крутить ненужные циклы и пожертвовать 90% скорости на ненужный мусор. Либо нагородить 100500 вызовов, делающих одно и то же, но чуть по-разному.
                                                                                                                                                                                                        –2
                                                                                                                                                                                                        С этим никто и не спорит. Можно просто взять и выбросить половину программы, работать будет быстрее, только не так как ожидалось. Я говорил, что компилятор не должен делать это молча. А спорю я потому что мне говорят, что это правильно и по-другому никак. И при этом нет особых доказательств кроме «в стандарте так, смиритесь».

                                                                                                                                                                                                        Кстати, вы можете привести пример таких оптимизаций? Нашел в гугле пару ссылок, попробую объяснить, что я имею в виду.
                                                                                                                                                                                                        Скрытый текст
                                                                                                                                                                                                        http://en.cppreference.com/w/cpp/language/ub#UB_and_optimization
                                                                                                                                                                                                        Signed overflow
                                                                                                                                                                                                        int foo(int x) {
                                                                                                                                                                                                            return x+1 > x; // either true or UB due to signed overflow
                                                                                                                                                                                                        }
                                                                                                                                                                                                        may be compiled as (demo)
                                                                                                                                                                                                        foo(int):
                                                                                                                                                                                                                movl    $1, %eax
                                                                                                                                                                                                                ret
                                                                                                                                                                                                        


                                                                                                                                                                                                        Это явно ошибка в логике — условие всегда истинно или происходит переполнение.

                                                                                                                                                                                                        Access out of bounds
                                                                                                                                                                                                        int table[4] = {};
                                                                                                                                                                                                        bool exists_in_table(int v)
                                                                                                                                                                                                        {
                                                                                                                                                                                                            // return true in one of the first 4 iterations or UB due to out-of-bounds access
                                                                                                                                                                                                            for (int i = 0; i <= 4; i++) {
                                                                                                                                                                                                                if (table[i] == v) return true;
                                                                                                                                                                                                            }
                                                                                                                                                                                                            return false;
                                                                                                                                                                                                        }
                                                                                                                                                                                                        May be compiled as (demo)
                                                                                                                                                                                                        exists_in_table(int):
                                                                                                                                                                                                                movl    $1, %eax
                                                                                                                                                                                                                ret
                                                                                                                                                                                                        


                                                                                                                                                                                                        Здесь происходит обращение за границу массива. Во первых, это ошибка в логике. Во-вторых, есть 3 возможных варианта — значение равно или не равно, или произойдет аппаратная ошибка чтения. Оптимизация предполагает, что там всегда будет равно, из-за чего до 2-го return не дойдет, то есть выбрасывает из рассмотрения остальные варианты. Ошибка скрыта, логическое поведение кода изменилось.

                                                                                                                                                                                                        std::size_t f(int x)
                                                                                                                                                                                                        {
                                                                                                                                                                                                            std::size_t a;
                                                                                                                                                                                                            if(x) // either x nonzero or UB
                                                                                                                                                                                                                a = 42;
                                                                                                                                                                                                            return a;
                                                                                                                                                                                                        }
                                                                                                                                                                                                        May be compiled as (demo)
                                                                                                                                                                                                        f(int):
                                                                                                                                                                                                                mov     eax, 42
                                                                                                                                                                                                                ret
                                                                                                                                                                                                        


                                                                                                                                                                                                        В коде присвоение 42 только при определенном условии, а после оптимизации во всех случаях. И опять же, неинициализированная переменная это ошибка в логике.

                                                                                                                                                                                                        https://www.cl.cam.ac.uk/teaching/1415/CandC++/lecture10.pdf

                                                                                                                                                                                                        By knowing that values “cannot” overflow, the compiler can enable useful optimisations:
                                                                                                                                                                                                        for (i = 0; i <= N; ++i) { ... }
                                                                                                                                                                                                        If signed arithmetic is undefined, then the compiler can assume the loop runs exactly N+1 times.
                                                                                                                                                                                                        

                                                                                                                                                                                                        Вот и пусть 'assume', потому что это то, что написано. Если там будет i <= INT_MAX, это будет известно на этапе компиляции, и можно показать предупреждение.

                                                                                                                                                                                                        Это между прочим те вещи, которые ищет PVS-Studio. Такая оптимизация маскирует ошибки, хотя задача компилятора эти ошибки находить. Программа работает быстро, но не так как надо.

                                                                                                                                                                                                        У меня сложилось впечатление, что UB называют очень логически разные ситуации, часть из которых подходит для оптимизации, часть нет. Если вы считаете, что я не прав, значит так тому и быть. Я просто высказал мнение и постарался объяснить, почему я так думаю.
                                                                                                                                                                                                          +1
                                                                                                                                                                                                          UB называют любые ситуации, когда результат выполнения инструкции не регламентирован в стандарте.
                                                                                                                                                                                                            –1

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

                                                                                                                                                                                                              0
                                                                                                                                                                                                              работать со всеми одинаково нельзя

                                                                                                                                                                                                              Что значит «одинаково работать»?
                                                                                                                                                                                                                0

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

                                                                                                                                                                                                                  0
                                                                                                                                                                                                                  Менять в любом из этих случаев код на любой другой, нужный для оптимизации, предполагая, что поведение будет такое же.
                                                                                                                                                                                                                  Извините, но кто будет решать — когда код менять можно, а когда — нет? И как?
                                                                                                                                                                                                                    0

                                                                                                                                                                                                                    Решать будет компилятор. Только надо разделить понятия "UB из-за неинициализированной переменной" и "UB так как возможно кто-то 8 байт пишет из другого потока". В первом случае это написано в коде, во втором нет.

                                                                                                                                                                                                                      +1
                                                                                                                                                                                                                      Только надо разделить понятия «UB из-за неинициализированной переменной» и «UB так как возможно кто-то 8 байт пишет из другого потока».
                                                                                                                                                                                                                      Они уже разделены. Я боюсь вы смешиваете два понятия: поведение, определяемое реализацией (что-то, что разные компиляторы могут делать по разному) и неопределённое поведение (то, чего в программе случаться не должно и то, чего программист не должен делать никогда).

                                                                                                                                                                                                                      В первом случае — компилятор обязан обеспечить некоторое разумное поведение (скажем если вы засунете 2147483648 в 32-битный int, то получите либо -2147483648, если у вас используется дополнительный код, либо -0, если у вас используется прямой код, но ничего «странного» при этом произойти не может), во втором — компилятор может делать всё, что угодно. Совсем что угодно.

                                                                                                                                                                                                                      Зачем вам потребовалось ещё как-то этот волос расщеплять и кому от этого станет легче — мне неведомо.

                                                                                                                                                                                                                      В первом случае это написано в коде, во втором нет.
                                                                                                                                                                                                                      Что значит «написано» и «не написано»? 8 байт кто пишет? И как? Пушкин? С того света, что ли? Конечно же в программе есть где-то код, где и эти 8 файт атомарно пишутся…
                                                                                                                                                                                                            +1
                                                                                                                                                                                                            Такая оптимизация маскирует ошибки, хотя задача компилятора эти ошибки находить.
                                                                                                                                                                                                            Задача компилятора — компилировать. Для нахождения ошибок — есть другие инструменты. То, что компилятор, по совместительству, является ещё и статическим анализитором и линтером — это хорошо, но это не является его основной работой!

                                                                                                                                                                                                            int foo(int x) {
                                                                                                                                                                                                                return x+1 > x; // either true or UB due to signed overflow
                                                                                                                                                                                                            }
                                                                                                                                                                                                            may be compiled as (demo)
                                                                                                                                                                                                            foo(int):
                                                                                                                                                                                                                    movl    $1, %eax
                                                                                                                                                                                                                    ret
                                                                                                                                                                                                            

                                                                                                                                                                                                            Это явно ошибка в логике — условие всегда истинно или происходит переполнение.
                                                                                                                                                                                                            Так имеет право компилятор сделать такую оптимизацию или нет?

                                                                                                                                                                                                            У меня сложилось впечатление, что UB называют очень логически разные ситуации, часть из которых подходит для оптимизации, часть нет.
                                                                                                                                                                                                            Вы, в некотором роде, правы. Часть вещей, которые названы UB в стандарте можно бы и доопределелить. Скажем то же переполнение можно рассматривать в дополнительном коде — и, сюрприз, сюрприз, вы можете это заказать.

                                                                                                                                                                                                            Все же UB, которые не «доопределены» подобным образом компилятор имеет право «использовать» — в смысле «трактовать в свою пользу».

                                                                                                                                                                                                            Я просто высказал мнение и постарался объяснить, почему я так думаю.
                                                                                                                                                                                                            Вы бы вначале сформулировали своё мнение, а потом его высказывали бы, а? А то сейчас у вас получается что компилятор должен и предполагать, что программа «глупостей не делает» (где я утверждал, что оптимизация должна гарантировать работу стороннего кода, считающего такты или что-то подобное?), и предполагать, что они «глупости там таки есть» ( считаю, что компилятор наоборот не должен полагаться на UB и должен оставлять его как есть). Одновременно.
                                                                                                                                                                              0

                                                                                                                                                                              Давайте возьмем гугл и посмотрим на производительность tcc от Bellard'а:
                                                                                                                                                                              https://groups.google.com/forum/#!topic/comp.lang.c/9l55qxm-S68, http://lists.nongnu.org/archive/html/tinycc-devel/2013-02/msg00039.html. Потери производительности от 2х раз до порядка (относительно gcc).


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


                                                                                                                                                                              Без этого банальный инкремент int'а будет занимать не одну инструкцию, а 3-4 с условным переходом, что будет забивать мозг предиктору переходов (который аппаратный и "память" у него короткая), дёргать дополнительно ALU для сравнения или читать дополнительно флаги. Всё это спокойно может давать overhead в десятки тактов.


                                                                                                                                                                              Или он выполняется одну инструкцию, но может, например, вызывать exception (аппаратный) при переполнении. Или дать неожиданный результат/повредить память где-то ещё, или что угодно.


                                                                                                                                                                              Но при выполнении контракта (недопущении UB программистом) вы получите работу инкремента за одну инструкцию.

                                                                                                                                                                                –1
                                                                                                                                                                                Без этого банальный инкремент int'а будет занимать не одну инструкцию, а 3-4 с условным переходом

                                                                                                                                                                                Я говорю про абсолютно обратную ситуацию. Написано сложить две переменных в памяти и поместить в третью — генерируем инструкции для сложения и присваивания, выдаем предупреждение о возможном переполнении. Или вообще не компилируем. Но не додумываем за программиста и не выкидываем просто так.


                                                                                                                                                                                более топорная кодогенерация

                                                                                                                                                                                Это не то, что я имел в виду.

                                                                                                                                                                                  +1
                                                                                                                                                                                  Написано сложить две переменных в памяти и поместить в третью — генерируем инструкции для сложения и присваивания, выдаем предупреждение о возможном переполнении.

                                                                                                                                                                                  После какого количества warning'ов вы их отключите? После первых нескольких тысяч? Или раньше?


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

                                                                                                                                                                                    –2

                                                                                                                                                                                    Я поменяю или тип или размер результата. А если не поменял, то пусть оно случается. Если вообще случится. Выкидывать такой код вообще или модифицировать его с другим поведением это неправильно.