1. C++ / Говнокод #23375

    +4

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    std::vector<int> vec = { 1, 2, 3, 4 };
    for (auto i = vec.size() - 1; i >= 0; i--) {
        cout << i << ": " << vec[i] << endl;
    }
    cout << endl;

    Выстрел в ногу, заботливо прикрытый фиговым листочком «auto».

    Запостил: gost, 30 Сентября 2017

    Комментарии (75) RSS

    • Так, у всех было :) Все равно компилятор выругается.
      Ответить
      • тут выругается. но в других местах - где сравнение и декремент не рядом стоят - может уже и пропустить.
        Ответить
      • Что характерно, с -Wall -Wpedantic gcc молчит как партизан:
        root@gost:~# g++ test.cpp -std=c++11 -Wall -Wpedantic -Werror -o test
        root@gost:~# ./test
        3: 4
        2: 3
        1: 2
        0: 1
        18446744073709551615: 0
        18446744073709551614: 33
        18446744073709551613: 0
        18446744073709551612: 0
        Segmentation fault


        И только -Wextra заставляет нас прозреть:
        root@gost:~# g++ test.cpp -std=c++11 -Wall -Wpedantic -Wextra -Werror -o test
        test.cpp: In function 'int main()':
        test.cpp:9:37: error: comparison of unsigned expression >= 0 is always true [-Werror=type-limits]
             for (auto i = vec.size() - 1; i >= 0; i--) {
                                             ^
        cc1plus: all warnings being treated as errors
        Ответить
    • есть определенная логика в том, что размер контейнера - беззнаковое

      хотя кто-то там на выступлении говорил аля "мы чет зря не сделали размеры контейнеров int'ами"
      Ответить
      • # зря не сделали размеры контейнеров int'ами

        Давно уже пора выпилить unsigned, а лучше запретить по закону
        Ответить
        • unsigned в некоторых случаях попросту быстрее, ибо не надо проверять против < 0
          Ответить
          • Может какраз наоборот? Int не надо проверять на переполнение, потому что это UB. Поэтому простор для оптимизаций для конпелятора больше.
            Ответить
            • Дак конпелятор и unsigned на переполнение не проверяет.
              Ответить
            • компилятор обычно считает что UB нет и выкидывает проверки на него как заведомо ложные. А вот всякие uint % 8 -> xor 0x7 справедливы только для беззнаковых
              Ответить
        • Java к вашим услугам
          Ответить
        • Знаковый тип позволит адресовать только половину доступных адресов. Это и есть та причина, по которой размер стандартных контейнеров без знаковый.
          Ответить
          • Нах тебе вектор размером с больше чем половина адресного пространства?
            Ответить
            • > Нах тебе вектор размером с больше чем половина адресного пространства?

              Если вдруг захочется сёрвить кусок поискового индекса, замапленного в память, с 32-битной машины. Сейчас amd64 повсеместен, и проблема уже не так актуальна, как лет 15 назад.
              Ответить
              • > кусок поискового индекса
                Ну раз всё равно кусок - значит можно на той же машине поднять второй инстанс (а может и ещё несколько, если PAE работает и физической памяти хватает).
                Ответить
              • Если уж хочется странного, то можно и два куска по 2 гб замапить.
                Ответить
          • Вообще никакой связи может не быть с размером адресного пространства и размером конкретного типа.
            Ответить
            • Кстати, да. Адрес не обязан быть линейным. Те, кто застал сегментированную память (DOS, Windows 3.x), подтвердят.
              Ответить
              • Я не уверен что кто-то создавал для ДОС компилятор соответствующий стандарту 98 года. Но если пофантазировать, то size_t мог бы быть ограничен в этом случае, как размером сегмента(на машинах, которые умеют только сегментную адресацию), так и размером всего адресного пространства(на машинах с плоской адресацией).
                ДОС работал и на тех и на других.
                Ответить
                • У реализаций Си/C++ для DOS (MS, Borland, Watcom) была модель памяти Huge, в которой массивы могли превышать размер сегмента. Там компилятор вставлял код для переключения сегмента, когда индекс переходит через границу сегментов.

                  Размер указателя был 32 бита и хранил он не линейный адрес, а запись из двух полей: сегмент + смещение. В упомянутых реализациях Си/C++ был даже нестандартный оператор :> для сборки указателя из сегмента и смещения.

                  В реальном режиме процессора (ДОС) эти 32 бита преобразовывались в 20-битный линейный адрес (сегмент тупо умножался на 16 и к нему прибавлялось смещение), а в защищённом (Windows) сегмент заменялся на базовый адрес из таблицы дескрипторов и к нему прибавлялось смещение, а результат мог занимать 32 бита. Но этот линейный адрес прятался где-то в глубинах процессора, а со стороны программ не был виден.

                  Так что к указателям было проще относиться как к «чёрному ящику».

                  Сегментная адресация применялась и на 32-битных машинах (PAE позволяла адресовать 64 гигабайта вместо 4), но уже считалась экзотикой.

                  *****

                  Так что в итоге делать с указателями в общем случае? У меня два варианта:

                  1. Считать указатель «чёрным ящиком», неким объектом фиксированного размера (не обязательно совпадающим с размером линейного указателя). Так было сделано в компиляторах для DOS и Windows 3.x.

                  2. Сделать указатели линейными, а в компилятор для машин с сегментной адресацией встроить транслятор, который будет преобразовывать указатели туда-сюда. Это негативно скажется на производительности.
                  Ответить
                  • Коллега, я застал еще HDD с шаговыми двигателями и объемом в десятки мб. Ты все почти правильно написал, но мой первый ПК 80286 физически имел шину адреса 24 бита, что позволяло ему адресовать в Дос больше памяти, используя 21 бит. Была такая штука himem.sys. С Дос и реальным режимом, кстати, не все так однозначно. Можно было его и защищенном режиме запустить. Или даже уйти в защищенный, настроить себе поддержку 32х битных, адресов и вернуться обратно. Кажется, dos4gw примерно для этого использовалась тем же компилятором watcom. В защищенном режиме в сегментные регистры помещаются селекторы дескрипторов, в которых в частности есть базовый адерес и лимит. При этом смещение внутри сегмента все также доступно из кода. Это та же segment:offset адресация, просто нет необходимости делить программу на несколько сегментов и адресоваться чем-то кроме смещения. То, что ты пишешь про глубины процессора справедливо для страничной адресации. С оговоркой, что трансляция адресов и в этом случае процесс управляемый, пусть на системном, а не на прикладном уровне.
                    К чему я это все. К тому, что к стандартному С++ это не имеет прямого отношения. Разработчики языка руководствуются двумя основными правилами: zero overhead abstractions and direct mapping to hardware. В следствии чего стандарт игнорирует сегментную адресацию, PAE и все такое. Есть абстрактное понятие storage, есть линейная адресация внутри него и есть типы, которые мапяться на регистры доступной разрядности. Я специально уточнил, что речь о стандарте языка(т.к. в примере кода стандартный контейней), которому всякие расширения или достандартные компиляторы не соответствуют просто по определению.
                    Ответить
                    • Выходит, что для модели памяти, оперирующей более сложными указателями, чем линейные, нельзя создать компилятор, соответствующий стандарту? Т. е. если мы поддерживаем сегменты, PAE и всё такое, то сознательно идём на нарушение стандарта?
                      Ответить
                      • Тут есть два момента. *Использование* PAE и сегментации не противоречит стандарту т.к. не меняет ни структуры указателе ни размера адресного пространства. Для прикладного программиста оно работает просто из коробки. А на amd64 уже не нужно ничего расширять(64бита хватит всем). С другой стороны код для *реализации* PAE и сегментации внутри ОС вряд ли может быть написан без нарушения стандарта. Но на самом деле это не критично. До 11 года стандарт С++ полностью игнорировал многопоточность, что не мешало ее использовать:)
                        Ответить
                        • Пруфани все, что написал.
                          Ответить
                          • Не вижу причин этим заниматься. Тут нет чего-то, что ты не сможешь сам проверить.
                            Ответить
                    • >>Можно было его и защищенном режиме запустить. Или даже уйти в защищенный, настроить себе поддержку 32х битных, адресов и вернуться обратно

                      Это "unreal mode". Говнохак, очень популярный в игрушках.

                      >>dos4gw
                      ЕМНИП dos4gw использовал API "expanded memory" а не unreal mode.

                      >>В защищенном режиме в сегментные регистры помещаются селекторы дескрипторов

                      В 32 да, но мне кажется что в long mode уже нет. Там только pages.

                      >>абстрактное понятие storage, есть линейная адресация внутри него
                      Согласен. Но если мы пишем для tiny mode (все сегментные регистры указывают на начало, а размер памяти ограничен 64К) то у нас линейное пространство:)
                      Ответить
                      • Ты точно не Сёма.
                        Ответить
                        • Сёма - это субару
                          А СёмаРеал - это бароп
                          а кто бароп - не знаю
                          Ответить
                          • Не, субару кресты знает, не похож он на сёму.
                            Ответить
                            • Иньо либо сам сема (и пытается отвести от себя подозрения), либо сам бароп (потому что тот постоянно в ком-то детектил сему).
                              Ответить
                              • Кстати, у тебя ник автолюбителя или анимешника?
                                Ответить
                                • читни сток, лол
                                  Ответить
                                • а разве есть существенная разница?
                                  голубой шильдик и оппозитный двигатель это уже диагноз так-то
                                  Ответить
                                  • Ну и что? У «Запорожца» вообще двигатель был в заднице. Но это же не означает, что у запорожцев с ориентацией что-то не так.
                                    Ответить
                                    • Ну... я вообще хотел проконстатировать, что у них там секта, это всем известный факт, они гордятся и первым и вторым.
                                      Но прочитав свой коммент второй раз, теперь тоже вижу явный гомосексуальный контекст.
                                      Ответить
                                      • Ну ориентация не только сексуальная бывает... Может быть, я имел в виду направление движения? Мало ли, задняя передача с передними перепутана...

                                        Кстати, а V-образный двигатель с развалом 180 градусов лучше оппозитного?
                                        Ответить
                            • Кто тогда Сёма?
                              это не я, инфа 100%

                              А кресты можно за год выучить
                              Ответить
                              • Действительно, кто? Гендерная Интрига...

                                Это не я.

                                За 20 дней же.
                                Ответить
                      • > но мне кажется что в long mode уже нет. Там только pages.
                        Селекторы это часть механизма защиты в защищенном режиме, они не должны никуда деться.

                        Я привел это в качестве примера вещей никак не связанных с С++. Ты понял, что я изначально писал конкретно про С++, про плюсовую memory model и типы завязанные на нее? Если да, то у тебя по прежнему есть возражения?

                        Вообще у меня есть такое наблюдение. Если задать вопрос джуну, то в ответ он выльет вот такой вот таз деталей реализации, про оффсеты и прерывания, баги MSVC, что у него скомпилилось, а что нет, без какой либо попытки ответить в терминах стандарта. Изначально вопрос стоял про стандартный вектор и рассматривать его в отрыве от стандарта, приплетая досовые компиляторы из 90х - дичайшее аматорство.
                        Ответить
                        • > без какой либо попытки ответить в терминах стандарта
                          Пока что ты не привел ни одного пруфа своему пездежу кроме сомнительной ссылки на анскильный бложик васянов из тулы.
                          Ответить
                          • Какой тебе нужен пруф, если я всего лишь указал, что решение *принятое* комитетом по стандартизации 20 лет назад не лишено смысла? Тебе нужен пруф, на то, что стандартная библиотека использует для хранения размера тип size_t. В этом заключается твоя претензия?
                            Ну чё ты такой сердитый человек-то, ну будьте людьми вы, ребята, я всегда вам говорю. Чего вы сразу начинаете?
                            Ответить
                            • Нет, ты сказал:
                              > Важно, что модель памяти С++ ограничивает тебя только адресами, которые помещаются в size_t.
                              > Просто стандарт С++ не поддерживает никаких "других" платформ.
                              Ответить
                            • Еще ты кажется намекал, что по стандарту память программы - это непрерывная последовательность байт.
                              Ответить
                              • "The memory available to a C++ program is one *or more* contiguous sequences of bytes. "

                                Выходит я был не прав, по поводу других платформ и непрерывной памяти. Признаю свою ошибку. Ты хамло, но спасибо, что обратил мое внимание на эти детали. Удивительно, как оно все это время ускользало... Пойду напьюсь теперь.
                                Ответить
                        • >>Селекторы это часть механизма защиты в защищенном режим
                          У меня для тебя дурные новости, чувак.
                          В середине восьмидесятых появилась страничная адресация, и теперь механизмы защиты есть и на уровне страницы тоже.

                          В long mode (aka x64) от сегментов практически избавились кроме парочки.

                          >>Если да, то у тебя по прежнему есть возражения?
                          Если ты почитаешь внимательнее, то увидишь что я с тобой не спорил.

                          Я совершенно согласен с тем, что С оперирует понятием "storage" и как должны вести себя указатели (в плане арифметики и разыменовывания) там тоже описано.

                          А как устроена памяти конкретной машины (есть там сегменты или еще что) -- это эквопенсиуально
                          Ответить
                          • > У меня для тебя дурные новости, чувак.

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

                            > Если ты почитаешь внимательнее, то увидишь что я с тобой не спорил.

                            Конкретно в этом сообщении нет, но чуть выше разве не ты писал?

                            > Вообще никакой связи может не быть с размером адресного пространства и размером конкретного типа.

                            Так ты согласился с моим утверждением или оно все еще кажется недостаточно точным?
                            В том случае, который я описал, есть связь или нет никакой?
                            Ответить
                            • >> В сегментные регистры помещаются селекторы, указатель на таблицу в отдельный регистр

                              И? Как это противоречит тому, что я написал выше?
                              В 286 были только сегменты для защиты
                              В 386 и сегменты и страницы (только сегменты современные ОСы перестали для этой цели юзать)
                              в x64 и вовсе случился:


                              "In 64-bit mode, segmentation is generally (but not completely) disabled, creating a flat 64-bit linear-address space. The processor treats the segment base of CS, DS, ES, SS as zero, creating a linear address that is equal to the effective address. The FS and GS segments are exceptions. These segment registers (which hold the segment base) can be used as an additional base registers in linear address calculations. They facilitate addressing local data and certain operating system data structures."

                              Где тут защита-то?

                              >>В том случае, который я описал, есть связь или нет никакой?
                              Тот случай, это который?
                              Это

                              "Знаковый тип позволит адресовать только половину доступных адресов. Это и есть та причина, по которой размер стандартных контейнеров без знаковый.
                              "?
                              Ты пытаешься объяснить почему size_t в стандарте беззнаковый?
                              Ответить
                              • Уфф. Я отвечал на коммент чувака, который написал, что в защищенном режиме с адресами все сложно. Я привел пример, где все понятно. И про страницы, кстати, тоже упомянул. Ты же решил поправить меня, указав мою цитату про сегментные регистры, которые и правда никак не связаны со страницами. Повторю, *сегментные регистры*, не сегментированная модель памяти а именно регистры. Переход в ring 0 осуществляется через загрузку в сегментный регистр селектора дескриптора с указанием нулевого кольца. Понятно, что процессы защищены друг от друга страничной адресацией. Но в доступе к аппаратному обеспечению, портам, привилегированным командам страничная адресация не участвует.
                                Участвуют сегментные регистры.

                                Да, но я не пытаюсь объяснить. Я уже объяснил, не только почему он беззнаковый, но и почему в векторе используется именно он. Казалось бы очевидная вещь, но не все понимают. Делюсь информацией, только и всего.
                                Ответить
                                • >>Повторю, *сегментные регистры*, не сегментированная модель памяти а именно регистры

                                  Вот да. Сегментация могла-бы в 32 mode юзаться для защиты памяти, но не юзается. А в 64 выпелена вовсе (точнее там как бы один большой сегмент размером со всю память).

                                  Но размер сегмента это всего лишь одно из полей дескриптора. С тем что другие поля (DPL там всякий, да собственно и поле с указанием разрядности) юзаются я не спорю.

                                  >не только почему он беззнаковый,
                                  Что мешало сделать его знаковым, а работу с отрицательными значением объявить UB?
                                  Ответить
                                  • Вероятно, желание дать возможность создавать контейнер максимально возможного размера. Знаковый тип при этом должен был бы иметь большую разрядность и еще не факт, что такой тип был бы доступен на целевой платформе. Кстати, мне тут уже объяснили, где был не прав. size_t завязан лишь на максимальный размер объекта и соответствует размеру указателя на большинстве, но не на всех платформах. Т.е. твое изначальное утверждение верно.
                                    Ответить
                                    • >>иметь большую разрядность и еще не факт, что такой тип был бы доступен на целевой платформе

                                      Я вот зацепился именно за это утверждение.

                                      Допустим у меня sizeof(int) == 32, при этом максимально доступное адресное пространство 2^16.

                                      Ну вот такая глупая система. Но что помешает мне ее создать?
                                      Ответить
                                      • На самом деле ничего не мешает создать такую систему, но стандарт же должен быть максимально generic. Чтобы можно было написать код, который оптимально использует ресурсы каждой системы. Для твоей системы int будет избыточен, для моей слишком мал, а на третьей типы больше чем size_t не доступны или дороги, бывает и такое. Поэтому целесообразно использовать тип, который гарантировано доступен и достаточен. Единожды написанный код отлично будет работать на твоей и на моей системе. Это просто более гибко. Еще sizeof возвращает размер в sizeof(char), а не в битах.
                                        Ответить
                                        • >>Еще sizeof возвращает размер в sizeof(char), а не в битах
                                          действительно. Ну тогда
                                          -32
                                          +4
                                          Ответить
                  • ЕМНИП, дело было не столько в модели памяти, сколько в типе указателя (по крайней мере в Борланд). Модель лишь определяла поведение указателей по умолчанию.

                    Давайте сразу оговоримся что адрес имел формат СЕГМЕНТ:СМЕЩЕНИЕ (оба по 16 бит) а как они мапились в реальные запросы (что выставлял на шину адреса проц) это отдельная песня.

                    Вот указатель мог быть ближним, и тогда он содержал только смещение.
                    При разыменовывании сегментный регистр никто не трогал, и потому если программист поменял значение сегментного регистра и забыл об этом, то указатель становился невалидным.
                    При адресной арифметике он просто перехлестывался через 64К делая невозможным хранение объектов большего размера.

                    Был far pointer, он имел еще и СЕГМЕНТ и перед разыменовыванием загружал его в регистр. Но адресная арифметика работала так же.

                    А был еще huge который умел менять сегментный регистр при адресной арифметике. Он был жутко тормозной, но позволял делать вид что он почти линейный.

                    При установке адреса на шину он "срезался" до 20 бит и потому можно было попасть в один и тот же кусок памяти разными способами. Говнокодеры полюбили этот прием, и когда адресную шину увеличили до 24 то оставили эмулятор старого поведения (см. Gate A20).


                    Товарищ внизу правильно говорит что с точки зрения стандарта это хуйня, но в стандарте не было понятия "far pointer".
                    Ответить
            • С другой стороны, знаковый тип для размера уменьшает вдвое максимальный размер.
              Ответить
            • Конкретно size_t существует как раз для создания такой связи. Иначе зачем было его добавлять? new operator принимает size_t, sizeof возвращает size_t, размер массива size_t и т.д.. Ограничения тут самым прямым образом зависят от количества доступных адресов. Если не в стандарте, то хотя бы тут можно почитать об эотм https://www.viva64.com/ru/a/0050/.
              Ответить
              • Петух, иди маны почитай.
                > std::size_t can store the maximum size of a theoretically possible object of any type (including array).
                > On many platforms (an exception is systems with segmented addressing) std::size_t can safely store the value of any non-member pointer, in which case it is synonymous with std::uintptr_t.
                Ответить
                • Странно, что ты смог найти правильные цитаты в стандарте, но не осилил связь между размером указателя и размером адресного пространства. Что ты хотел этим опровергнуть?
                  Ответить
                  • Эти цитаты должны были донести до тебя мысль, что возможность size_t вместить в себя указатель на некоторых платформах - не более, чем совпадение, и size_t не "существует как раз для создания такой связи".
                    Ответить
                    • Важно, что модель памяти С++ ограничивает тебя только адресами, которые помещаются в size_t. Разве есть хоть какой-то аргумент в пользу использования знакового типа при создании универсального контейнера, если 1) он должен иметь разрядность больше size_t, чтобы не ограничиваться половиной адресов 2) не факт, что платформа вообще поддерживает такой тип?
                      Такое может предложить только петух или питонист.

                      Тут нет совпадения. Просто стандарт С++ не поддерживает никаких "других" платформ. Это не значит, что их нет. Просто понятие адреса определено, без учета их особенностей.
                      Ответить
                      • Ой, ламер, все.
                        Ответить
                        • Да какая в пизду разница, вмещает size_t все адреса, или нет, на каких-то сферических платформах в вакууме? На моем компе работает - идите нахуй.
                          P.S. Я тоже ламер.
                          Ответить
                          • Как говорил Царь, единственная вменяемая платформа — это gcc + Linux + x86_64 (amd64). Остальные не нужны.
                            Ответить
                          • работает на таргете - посылай всех в пизду
                            а коли не пашет - сам иди на хуй
                            Ответить
                          • Я тоже всегда говорю "у меня работает и неебет".
                            Потом оказывается что я захардкодил "c:\documents and settings\semareal" в семи местах
                            Ответить
                            • > захардкодил "c:\documents and settings\semareal" в семи местах
                              Вот ты пошутил, а мне с этим работать на работе приходится.
                              Ответить
                              • лол) а у вас CI нету?
                                Ответить
                                • А нах он? Тестов-то нет)
                                  Ответить
                                  • А язык какой, кстати?
                                    Ответить
                                    • Прости, боюсь говорить. Вдруг коллеги сидят на говнокоде и сдеанонят по языку.
                                      (не пхп)
                                      Ответить
                                      • Такой редкий язык, что сразу сдеанонят?
                                        На Dart пишете?
                                        Ответить
                                        • > Такой редкий язык, что сразу сдеанонят?
                                          Да уже сдеанонили, походу. Они же тоже знают, что язык редкий и пути захардкоженные в проекте есть.
                                          Ответить
                    • Скажем так:

                      я могу изобрести копелятор в котором sizeof(short) == sizeof(long) == 8
                      а sizeof(size_t) == 64

                      Это тупо,нобудет же работать
                      Ответить
                • Не ссорьтесь, крестопетухи
                  Ответить

    Добавить комментарий