1. Си / Говнокод #25375

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    // https://git.zx2c4.com/BruteZip/tree/read.c?id=e4e9c17b99e0d108136b8a07632b1ebaa7d09d28#n26
    
    int main(int argc, char *argv[])
    {
    	union {
    		long int l;
    		char c[sizeof(long int)];
    	} u;
    	u.l = 1;
    	if (u.c[sizeof(long int) - 1] == 1) {
    		printf("This program only runs on little endian archs, because I'm lazy. Sorry.\n");
    		return -2;
    	}

    Хуйня какая-то. Ведь sizeof(long int) может быть равен sizeof(char).

    Но над такой питушней обычно никто не задумывается

    Запостил: j123123, 13 Февраля 2019

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

    • https://govnokod.ru/15707 хуйню с определением endian я уже когда-то вбрасывал
      Ответить
    • Можно ли считать этот ыореант кросплатформиным?
      union {
              int16_t petyx;
              int8_t petyshki[2];
          } petyxu;
          petyxu.petyx = 1;
          if (petyxu.petyshki[0] != 1) {
              puts("TY PETIX!!!");
          }
      Ответить
      • Не работает на платформах без «int8_t» или «int16_t», проверь.
        Ответить
      • показать все, что скрытоvanished
        Ответить
        • Разве петикс и петишки не будут занимать одну и ту же память?
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • Хотелось бы увидеть примеры самых странных вещей, которые получаются из-за «type punning».

              Перемешивание байтов из-за «big engian», «little endian», «middle endian» меня не интересует, это и так понятно.

              Коверкание чисел из-за копирования целого питуха в плавающего и обратно меня тоже не интересует. Видел я алгоритм с «обратным квадратным корнем» (0x5F3759DF), меня этим не запугать.

              Что ещё бывает?
              Ответить
              • показать все, что скрытоvanished
                Ответить
                • Допустим. Стандарт позволяет в целях выравнивания добавлять незначащие байты перед структурой?
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • Ну то есть можно гарантировать, что первые байты скалярных членов union'а и первые байты первых полей структур, также являющихся членами того же union'а, будут накладываться?
                      Ответить
                      • показать все, что скрытоvanished
                        Ответить
                      • показать все, что скрытоvanished
                        Ответить
                        • То есть стандарт позволяет для реализации union'а использовать произвольный контейнер? Его члены можно упаковать в какой-нибудь архив (в TAR или в контейнер OLE2) или там есть ограничения на sizeof?
                          Ответить
                          • показать все, что скрытоvanished
                            Ответить
                            • > в разные углы памяти распихкать
                              нахуй такой юнон, я всегда считал, что это чтоб память экономить для данных, которые не юзаются одновременно, и чтоб юзать как reinterpret_cast в крестах.
                              Ответить
                              • Вот кстати, существует ли в сишке адекватная замена reinterpret_cast? memcpy/memmove не предлагать. Про каст указателей я знаю, но от него у меня звёздочки в глазах.
                                Ответить
                                • показать все, что скрытоvanished
                                  Ответить
                                  • Но у него хотя бы синтаксис приличный.
                                    Сравни:
                                    rooster = reinterpret_cast<char>(chick);

                                    И вот этот ужас:
                                    rooster = *(char *)&chick;
                                    Ответить
                                    • #define REINTERPRET_CAST(From, To, value) ((union{From from; To to;}){.from = value}.to)
                                      printf("%x", REINTERPRET_CAST(float, int, 3.14));
                                      Ответить
                                    • показать все, что скрытоvanished
                                      Ответить
                                      • Нет, в си сделали не плохо, а ещё хуже:
                                        int x = (int)3.14;
                                        float y = (float)3;
                                        printf("%d\n", x);
                                        printf("%f\n", y);

                                        Вот тут, например, и не «посмотреть по-другому» (reinterpret_cast), и не «расширение/сужение», тут полноценное преобразование (с кучей арифметических действий).

                                        Я считаю, что (int) не нужен. Лучше явно использовать floor/ceil/round/что-то там ещё, чтобы показать, как именно ты хочешь округлить.
                                        Ответить
                            • Для сравнения: в стандартном «Паскале» не было отдельных union'ов, там были записи с вариантными полями. Самое последнее поле записи могло ветвиться (естественно, в это поле можно было упихать и сложные типы вроде вложенных записей и массивов), а перед ним должно было стоять специальное поле-дискриминант, которое показывает, какой из вариантов в настоящий момент используется:
                              type
                                Vorent = (eAge, eIq, eWeight); (* это enum *)
                                Petux = record
                                  what: Vorent; (* это дискриминант *)
                                  case what of (* а это начало того самого union'а *)
                                      eAge: (* если what = eAge, используем этот вариант *)
                                          age: byte;
                                      eIq: (* если what = eIq, используем этот вариант *)
                                          iq: byte;
                                      eWeight: (* если what = eWeight, используем этот вариант *)
                                          weight: byte;
                                end; (* end закрывает и case, и record *)

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

                              Компания «Борланд» при реализации своего диалекта решила на дискриминант положить болт. В «Турбо Паскале» и в его наследниках можно в любой момент использовать любой вариант (как в «сишке» можно использовать любой член союза):
                              type
                                Petux = record
                                  case integer of (* можно написать любой идентификатор поля или типа *)
                                  (* всё равно он будет проигнорирован компилятором *)
                                      42: (* здесь можно написать любое значение *)
                                  (* всё равно его никто не проверит *)
                                          age: byte;
                                      'Q': (* можно даже указать константу другого типа *)
                                          iq: byte;
                                      false: (* эти константы просто разделяют варианты *)
                                          weight: byte;
                                end; (* end закрывает и case, и record *)

                              И в «Турбо Паскале» уже можно записать значение в petookh.age, а прочитать из petookh.iq, хотя «Standard Pascal» этого не позволял.
                              Ответить
                        • У меня появилась идея. А давайте второй член союза хранить циклически сдвинутым на один бит (ROR 1), чтобы при извлечении нужно было сдвигать обратно (ROL 1); у третьего члена уже делать сдвиг на два бита и так далее, каждый последующий член сдвигать ещё на один бит.

                          union petux {
                            age char;
                            iq char;
                            weight char;
                          };
                          union petux petookh = {0};
                          petookh.age = 42;
                          printf("%d\n", petookh.iq); // выведет 84
                          printf("%d\n", petookh.weight); // выведет 168


                          Стандарт я этим не нарушу?
                          Ответить
                        • > может быть что iq у него стал 96. Или 33. Или 22. Хотя почти всегда он станет 42

                          Всегда 42.

                          http://eel.is/c++draft/class.union (в других стандартах примерно то же самое)
                          > If a standard-layout union contains several standard-layout structs that share a
                          > common initial sequence ([class.mem]), and if a non-static data member of an
                          > object of this standard-layout union type is active and is one of the standard-layout
                          > structs, it is permitted to inspect the common initial sequence of any of the
                          > standard-layout struct members

                          У тебя в юнионе оба члена одинакового типа и они полностью занимают common
                          initial sequence
                          . Это то же самое, что сделать мемсру из одного в другого.
                          Ответить
          • Надо посмотреть, разрешает ли стандарт выравнивание элементов массива. Вроде бы элементы массива должны идти плотно, но я не уверен.

            Выравнивание полей структуры он разрешает, поэтому при описании структур приходится вставлять директивы компилятора (#pragma, __attribute__), чтобы не выравнивал. У некоторых ЯП даже было слово «packed» для этого.
            Ответить
        • Слишком много софта, привязанного к играм с юнионом. Юнионы используют для сериализации данных, отправляемых по сети или записываемых в файл. Если кококококой-нибудь кококококомпилятор будет что-то вставлять перед элементом юниона, то в нём нельзя будет скомпилировать заметную часть ПО.
          Ответить
    • именно поэтому я за Rust,
      i8,i16,i32,i64,i128
      u8,u16,u32,u64,u128
      isize,
      usize

      и никакой питушни с этими вашими sizeof
      Ответить
    • И вообще проверка рахит-тинктуры должна производиться в компайл-тайме.
      Ответить
      • В S" Forth" я могу включать/выключать компай тайм когда захочу. Именно поэтому я за S" Forth".
        Ответить
    • показать все, что скрытоvanished
      Ответить
      • Кто о чём, а ты о хуях.

        На DSP, где один октет адресовать нельзя в принципе. У них в «байте» может быть 16, 32 или даже 64 бита, чего вполне хватает для того, чтобы вместить целый long int.

        Были ещё древние процессоры с 24-битными, 32-битными, 36-битными, 48-битными словами, не разбитыми на байты. У «PDP-10» известный компилятор «Си» разбивал 36-битное слово на 9-битные «байты» (нонеты), но другие компиляторы могли и не разбивать.
        Ответить
        • показать все, что скрытоvanished
          Ответить
        • показать все, что скрытоvanished
          Ответить
          • Опять ты о хуях.

            char бывает разным. Он не обязан быть восьмибитным. Точно так же long int не обязан вмещать несколько чаров.

            Тебе другой гость написал нестрогое неравенство, из которого следует, что long int может совпасть с чаром.

            Были какие-то реализации, авторы которых поняли слово «char» буквально и решили уместить в него юникодный символ.
            Ответить
          • показать все, что скрытоvanished
            Ответить
            • Ещё в BCD = binary-coded decimal. Это когда в каждом байте или в каждом ниббле байта (в каждой тетраде) хранится десятичная цифра.
              Ответить
              • показать все, что скрытоvanished
                Ответить
                • Микрокалькуляторы прямо в BCD и считали, чтобы не заморачиваться с перекодированием чисел при выводе на экран.

                  Ещё у IEEE754 есть инструкции для двоично-десятичных данных.

                  А у IBM/370 был шестнадцатеричный плавающий питух:
                  https://en.wikipedia.org/wiki/IBM_hexadecimal_floating_point
                  Там порядок означал не на сколько битов нужно сдвинуть мантиссу, а на сколько шестнадцатеричных цифр.
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • Джва пальца это слишком маленькая разрядность. Именно поэтому я за 64 пальца.
                      Ответить
                      • показать все, что скрытоvanished
                        Ответить
                      • –— Я всех умней! —– кричит петух. –—
                        Умею я считать до двух!
                        —– Подумаешь! –— ворчит хорёк. —–
                        А я могу до четырёх!
                        –— Я —– до шести! –— воскликнул жук.
                        —– Я –— до восьми! —– шепнул паук.

                        Тут подползла сороконожка:
                        –— Я, кажется, умней немножко
                        Жука и даже паука —–
                        Считаю я до сорока!

                        —– Ах, ужас! –— ужаснулся уж. —–
                        Ведь я ж не глуп. Но почему ж
                        Нет у меня ни рук, ни ног,
                        А то и я считать бы мог!

                        А у меня есть карандаш.
                        Ему что хочешь, то задашь.
                        Одной ногой умножит, сложит.
                        Всё в мире сосчитать он может!
                        Ответить
            • показать все, что скрытоvanished
              Ответить
              • показать все, что скрытоvanished
                Ответить
              • Ты тут первый заговорил про «не степень двойки». Поясни свою мысль.
                Ответить
                • показать все, что скрытоvanished
                  Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • Вроде char должен как минимум вмещать весь набор символов, которыми набрана сама программа. Там меньше семи битов никак не получается (можно было бы уложиться в шесть, если бы сишка была регистронезависимой).
                      Ответить
                      • P.S. Это свойство окажется полезным для раскрутки компилятора: чтобы на сишке написать компилятор сишки. Без этого свойства парсинг будет выглядеть странновато...
                        Ответить
                      • > весь набор символов
                        Допустим, я заюзал юникодные смайлики в строковых литералах. В чар они не входят. ЧЯНТД?
                        Ответить
                        • A byte is at least large enough to contain any member of the basic execution
                          character set (5.3) and the eight-bit code units of the Unicode UTF-8 encoding form […]

                          § 4.4, 1

                          The basic source character set consists of 96 characters: the space character,
                          the control characters representing horizontal tab, vertical tab, form feed, and
                          new-line, plus the following 91 graphical characters […]

                          § 5.3, 1
                          Ответить
                      • Стандарт рассматривает source и execution наборы символов (5.2.1 Character sets). Каждый из них должен вместить как минимум basic набор (52 буквы, 10 цифр и 29 закорючек).

                        5.2.1.2 Multibyte characters
                        The basic character set shall be present and each character shall be encoded as a single byte.

                        Т.е. таки минимум 7 бит независимо от кодировок.
                        Ответить
                        • Значит, я могу использовать две десятичные цифры (96 < 100), но не могу обойтись четырьмя троичными (96 > 81)?
                          Ответить
              • bormand где-то в глубинах Стандарта раскапывал, что бит в крестах (и сишке) может иметь только два состояния, так что увы.
                Ответить
              • Ага, вот:
                [...] The representations of integral types shall define values by use of a pure binary numeration system. [...]

                § 6.9.1, 7

                A positional representation for integers that uses the binary digits 0 and 1, in which the values represented
                by successive bits are additive, begin with 1, and are multiplied by successive integral power of 2, except
                perhaps for the bit with the highest position. (Adapted from the American National Dictionary for Information
                Processing Systems.)

                § 6.9.1, 7, сноска 52.
                Ответить
                • Хм, а в C99, если я правильно его читаю, было требование только на unsigned char и битовые поля. А всё остальное implementation defined.

                  Ну и bit - unit of data storage in the execution environment large enough to hold an object that may have one of two values.
                  Ответить
      • показать все, что скрытоvanished
        Ответить
        • Нихуя не понял, "bormand" говорил, что это какое-то параллельное присваивание.
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • Угу, в некоторых компиляторах для 16-битных платформ sizeof(int) == sizeof(short int) == 2, а в некоторых компиляторах для 32-битных платформ sizeof(int) == sizeof(long int) == 4. То есть int с чем-нибудь по размеру совпадает, но нужно угадать, с чем именно на этот раз.

              По такой логике на 64-битной машине надо было вообще делать sizeof(int) == 8.
              Ответить
              • показать все, что скрытоvanished
                Ответить
              • показать все, что скрытоvanished
                Ответить
                • Да, кстати, в «Юниксе», пытаясь абстрагироваться от железа, выдумали какую-то глупость с типами без явного размера.

                  Но ведь при сохранении данных в файл или при отправке по сети использовался явный размер. Допустим, у меня есть формат графического файла с плотностью 24 бита на пиксель (каналы R, G, B по 8 бит). Если я вместо 24 битов запишу 16, 32 или 64, аргументируя это тем, что такой размер инта на моей машине, то этот файл никто не сможет прочитать (точнее, смогут только те, кто компилировал вьюер тем же компилятором). Значит, мне придётся использовать низкоуровневую питушню, в которой я могу размер указать явно.

                  Почему они так сделали?
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • У формата «TIFF» может быть сигнатура «II» или «MM». Угадайте с одного раза, что она означает.

                      Так вот, «II» означает «Иинтел», т. е. числа, не помещающиеся в байт, записывали адепты маленького конца.

                      «MM» означает «Ммоторола», т. е. числа, не помещающиеся в байт, записывали адепты большого конца.

                      Естественно, как на машине с «little endian», так и на машине с «big endian» можно было посмотреть порнокартинки обоих форматов, просто при просмотре файлов с сигнатурой «MM» на машине с интеловским процессором теоретически чуть-чуть снижался пирфоманс из-за необходимости менять порядок байтов. Хотя какой к чёрту пирфоманс из-за порядка байтов, когда основное время съедает ввод-вывод или разжатие?
                      Ответить
                    • > мне не важен размер
                      Т.е. тебе вообще похуй на результат твоей проги?

                      Есть 3 стула:
                      1) Для корректной работы проги тип должен вместить нужные мне числа. И мне похуй, как конпелятор его реализует.
                      2) Для корректной работы проги тип должен иметь указанные мной размер и представление.
                      3) Мне нужен самый эффективный тип на данной платформе (для длинной арифметики, к примеру).

                      int описывает только третий вариант. А этот вариант в реальном коде встречается гораздо реже первого...

                      З.Ы. Разрабатывая код, я вообще не знаю что я могу засунуть в int т.к. минимальный размер инта описан в informative приложении к стандарту, которое никого ни к чему не обязывает.
                      Ответить
                      • показать все, что скрытоvanished
                        Ответить
                      • Ещё в эпоху «DOS» было немало случаев, когда случайно оказывалось, что программа не может прочитать половину файла, потому что компилятор по умолчанию использует «signed». Разработчикам приходилось оперативно добавлять квалификатор «unsigned» в 100500 мест, чтобы поднять лимит.

                        Это одно из следствий похуизма.
                        Ответить
                      • показать все, что скрытоvanished
                        Ответить
                      • > 3) Мне нужен самый эффективный тип на данной платформе (для длинной арифметики, к примеру).

                        > int описывает только третий вариант.

                        Нет, не описывает. На x86-64 у тебя будет 32-битный int, но для эффективной длинной арифметике на данной платформе лучше взять 64-битный тип
                        Ответить
                      • > З.Ы. Разрабатывая код, я вообще не знаю что я могу засунуть в int т.к. минимальный размер инта описан в informative приложении к стандарту, которое никого ни к чему не обязывает.

                        Нет, это не в informative. Ну может в каких-то старых и так, но давай взглянем на C17 :

                        https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#subsection.6.2.5


                        > 6.2.5 Types

                        > A "plain" int object has the natural size suggested by the architecture of the executionenvironment (large enough to contain any value in the range INT_MIN to INT_MAX as defined in the header <limits.h>).

                        https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#subsection.5.2.4


                        > 5.2.4.2.1 Sizes of integer types <limits.h>

                        > Moreover, except for CHAR_BIT and MB_LEN_MAX, the following shall be replaced by expressions that have the same type as would an expression that is an object of the corresponding type converted according to the integer promotions. Their implementation-defined values shall be equal or greaterin magnitude (absolute value) to those shown, with the same sign.
                        Ответить
                        • Хм, и правда. А нахуя они в С99 джва раза эти размеры описали. Один раз в этом разделе и второй - в приложении.

                          Т.е. int это int16_least_t. Ок.
                          Ответить
                        • Кстати, раз UCHAR_MAX должен быть как минимум 255, то CHAR_BIT по этому стандарту менее чем 8-битным быть не может (потому что 255 тупо не влезет в 7-битный байт)
                          Ответить
                        • Вот что реально хотелось бы - набор интов с фиксированной длиной (одинаково работающих для всех платформ) и компайл-тайм флажки про emulated operations и emulated storage для этих типов.

                          Например на арме, если я не туплю, uint8 будет с флагом emulated operation но без emulated storage. А на какой-нибудь DSP'шке он получит и emulated storage.
                          Ответить
                          • З.Ы. Ну и возможно режимы unchecked/checked/saturated для знаковых чисел. Опять же с одинаковой реализацией для всех платформ и флажками про их нативную поддержку.
                            Ответить
                • показать все, что скрытоvanished
                  Ответить
                • Особенно красиво там смотрится DWORD64.
                  Ответить
                • показать все, что скрытоvanished
                  Ответить
    • показать все, что скрытоvanished
      Ответить
    • > Ведь sizeof(long int) может быть равен sizeof(char).

      Тем, кто пишет под такие вот платформы, думаю, не стоит париться о кросс-платформенности.
      Ответить
    • Надо написать самый ебанутый компилятор
      который полностью соответствует стандарту
      но при этом реализует все как попало - 7 бит в байте, 3 байта в инте, поля структуры хранятся в случайном порядке с паддингом, нулевой указатель имеет представление 42.
      И заставить, чтобы все программы могли компилироваться под этот компилятор и работать.
      Ответить
      • Crazy C Compiler
        Ответить
        • Смотри, чувак написал ебанутый компилятор сишки, выхлоп которого содержит только инструкции MOV:
          https://github.com/Battelle/movfuscator
          Ответить
          • Статья о тьюринг-полноте инструкции «mov»:
            https://www.cl.cam.ac.uk/~sd601/papers/mov.pdf

            Автор даже изобрёл ветвление на mov'ах. Пример:
            mov [di], 0
            mov [si], 1
            mov ax, [di]

            В результате выполнения в ax будет лежать 1, если si=di, и ноль, если они не равны.
            Ответить
            • Автор реально крейзи: «Removing all but the mov instruction from future iterations of the x86 architecture would have many advantages: the instruction format would be greatly simplified, the expensive decode unit would become much cheaper, and silicon currently used for complex functional units could be repurposed as even more cache. As long as someone else implements the compiler».
              Ответить
          • Этот чувак реально крэйзи:https://github.com/Battelle/reductio/blob/master/README.md
            Ответить
            • Обобщённое программирование, устранение дублирования кода.
              Ответить
            • Я вот ещё что придумал: к сожалению, инструкцию MOV для x86 нельзя представить в ASCII-кодах, зато инструкции AND, SUB, XOR представить в ASCII-кодах можно, причём как с непосредственным аргументом, так и с байтом mod r/m.

              Надо придумать эффективный способ выражения MOV через комбинации этих трёх инструкций.

              На поверхности такой вариант: обнуляем регистр с помощью XOR или SUB самого с собой, потом с помощью XOR кладём в него новое значение.
              Ответить
              • Он меня опередил. У него есть постпроцессоры, умеющие заменять MOV на другие инструкции:
                ## XORfuscator
                
                x86 xor is Turing-complete, so the XORfuscator translates programs into XOR
                instructions, and only XOR instructions.
                
                ## SUBfuscator
                
                Translates programs into only SUB instructions.
                Ответить
            • показать все, что скрытоvanished
              Ответить
      • > 3 байта в инте
        31 бит в инте. А то получается, что padding биты в стандарте зря описаны и нигде реально не встречаются...
        Ответить
        • З.Ы. А в padding битах будет лежать CRC4 от данных. И все операции будут убивать прогу если эти биты некорректны. Битоёбы должны страдать.
          Ответить
          • Отрицательные числа запишем в формате sign+magnitude. Причём magnitude поксорено с магической константой и провёрнуто циклическим сдвигом. Ибо нехуй кастовать.
            Ответить
      • Я бы ещё все символы basic execution set перемешал в случайном порядке. Стандарт не запрещает.
        Ответить
      • В указатели добавим случайные биты (равные для указателей на один объект). Ибо нельзя сравнивать указатели на разные объекты.
        Ответить
      • Ну и зависимость всех этих битностей и т.п. от фазы луны и положения звёзд - в полнолуние байт 13 бит, к примеру. Чтоб не хардкодили размеры.
        Ответить
        • …А всем недовольным комментаторы с Хабра будут писать, что это низкоуровневый язык и тут так принято, а кому это не нравится — тот быдло тупое!
          Ответить
      • >> который полностью соответствует стандарту
        >> но при этом реализует все как попало

        Итальянская забастовка!

        https://ru.wikipedia.org/wiki/Итальянская_забастовка
        Ответить
      • > но при этом реализует все как попало - 7 бит в байте

        Не выйдет, UCHAR_MAX нельзя будет записать. Он должен быть минимум 255.
        Ответить
    • показать все, что скрытоvanished
      Ответить

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