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

    +133

    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
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    #ifndef ORDER32_H
    #define ORDER32_H
    
    #include <limits.h>
    #include <stdint.h>
    
    #if CHAR_BIT != 8
    #error "unsupported char size"
    #endif
    
    enum
    {
        O32_LITTLE_ENDIAN = 0x03020100ul,
        O32_BIG_ENDIAN = 0x00010203ul,
        O32_PDP_ENDIAN = 0x01000302ul
    };
    
    static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
        { { 0, 1, 2, 3 } };
    
    #define O32_HOST_ORDER (o32_host_order.value)
    
    #endif

    Говнокод из http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine
    Мало того, что писать в один тип из юниона и потом читать из другого это UB, так еще компилятор (в случае GCC) из

    int main(void)
    {return O32_HOST_ORDER == O32_LITTLE_ENDIAN;}

    нагенерирует код
    main:
    xor eax, eax
    cmp DWORD PTR o32_host_order[rip], 50462976
    sete al
    ret
    o32_host_order:
    .byte 0
    .byte 1
    .byte 2
    .byte 3

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

    Запостил: j123123, 08 Апреля 2014

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

    • > писать в один тип из юниона и потом читать из другого это UB
      ЩИТО? Можно цитату из стандарта?
      Ответить
      • http://www.open-std.org/jtc1/sc22/wg14/www/docs/9899-1999_cor_2-2004.pdf

        Page 38, 6.2.6.1
        Change paragraph 7 to:

        When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.
        Ответить
        • >unspecified values.
          Так мы по этим unspecified values и хотим понять, какая же endianness. Это не то же самое, что UB.
          Ответить
          • Не нужно по ним ничего понимать, лучше понимать это из таргета, под который компилируется код. Если мы сначала записали в один член юниона, потом прочитали из другого, компилятор имеет право сгенерировать код, который что угодно прочитает. Т.е. никто ничего не гарантирует. Что и есть неопределенное поведение
            А если у нас будут еще и структуры в юнионе, в структурах может быть разный alignment на разных компиляторах (и даже разных таргетах одного компилятора) и соответственно там могут быть дырки, всякие хитрые перекрытия полей структур, стандарт это поведение никак не регламентирует
            Ответить
            • Да и в любом случае о endianess хочется знать уже в компайл-тайме или даже в препроцесс-тайме. В рантайме - слишком поздно и неэффективно.
              Ответить
            • > Что и есть неопределенное поведение

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

              погугли "sysv abi x86-64" pdf'ку.
              Ответить
              • Неопределенное поведение - это невозможность нормально портировать. Если портабельность не нужна - можно и на асме писать.
                Ответить
                • "That escalated quickly." (c)
                  Ответить
                • Единственное преимущество Си - это портабельность?
                  Ответить
                  • Одно из, по сравнению с асмом.
                    Ответить
                    • Вот именно. Не надо забывать и про более высокий уровень абстракции, который дает Си.
                      А так бы ведь пересели с асма на что-то по типу LLVM IR. Портабельно же.
                      Ответить
                      • Бля, что мы обсуждаем? Речь зашла о том, что в конкретной комбинации железо-ось-конпелятор UB вполне определено, я написал, что это ломает одно из важнейших преимуществ це - портабельность.
                        Ответить
                        • В тех местах, где так важно кастовать типы куда-попало портабельность не особо нужна. В конце-концов этот каст как раз и нужен ради какой-то особенности платформы. Тут проще нахуячить десяток ифдефов под разные платформы/компилеры и набор тестов к ним. А в мало-мальски высокоуровневом коде таких потребностей уже и не возникает.
                          Ответить
                          • > и набор тестов к ним.
                            Кросскомпиляцию можно забыть?
                            Ответить
                            • > Кросскомпиляцию можно забыть?
                              Ну тесты соберешь кросскомпилером и на таргете прогонишь. Если там найдется чем их запустить :)
                              Ответить
          • О, ещё один анимешник.
            Ответить
            • Там вроде флешмоб был, когда все ставили анимешные аватарки.
              Ответить
            • ЛиспГовно тоже анимешник. Какой багор )))
              Ответить
        • >When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.

          Вообще это какая-то мутная и неоднозначная фразочка.
          Если у нас есть юнион типа

          union {
                  uint32_t value;
                	unsigned char bytes[4];
              } be = {0xffffffffull};
              
          // по-идее сейчас во всех be.bytes должно быть 0xff
          // т.к. те четыре байта относятся к этому типу (независимо от big/little endian)
          // поменяем один байт
              be.bytes[0]=0x00;
          // Поменян был один байт, но т.к. этот байт делит часть памяти от типа uint32_t, тогда получается что в uint32_t у нас unspecified value, а следовательно и в be.bytes[1..3].

          Черт знает что
          Ответить
          • Конкретно в этом предложении сказано, что например в коде
            union {
                    uint32_t value;
                    unsigned char bytes[5];
                } be;
            be.bytes = {0,1,2,3,4};
            be.value = 0;
            в be.bytes[0] или be.bytes[5] в зависимости от индусности машины будет неизвестное значение после присвоения be.value = 0;

            Что намекает нам ещё раз вопреки практике о бесполезности присвоений и чтений в\из разных членов, тк нет гарантии сохранения нетронутого байта в первозданном виде (в том виде, что до присвоения be.value = 0;).

            Прозаично, но сишники обожают юзать юнионы для разруливания strict aliasing и записи по частям.
            Ответить
      • https://stackoverflow.com/questions/11639947/is-type-punning-through-a-union-unspecified-in-c99-and-has-it-become-specified
        Ответить
      • Я кстати подозреваю, что UB-шность связана именно big-little endian (ну и с выравниванием)

        В некоторых случаях этот самый юнион используется как раз для выравнивания, чтобы не использовать гцц-шные атрибуты и прагма пак (их в стандарте точно нет) https://github.com/radare/radare2/blob/086fc30e3bee81cee3eb7678757443beda46d42a/shlr/sdb/src/cdb_make.c#L13
        Ответить
        • Ну т.е. способов трактовать одну область памяти двумя способами в сишке тупо нет (кроме memcpy, но с ним это уже не одна область, а тупо копия).

          Ок, будем знать :)
          Ответить
          • судя по ответам на stackoverflow это спорно. Одни пишут что
            [quote]The behavior of type punning with union changed from C89 to C99. The behavior in C99 is the same as C11.

            As Wug noted in his answer, type punning is allowed in C99 / C11. An unspecified value that could be a trap is read when the union members are of different size.[/quote]
            Другие отвечают что примечание не имеет юридической силы.
            Ответить
            • Я подозреваю, что в командах разработчиков компиляторов есть специальные тренированные евреи. Ведь пытаться понять сей стандарт - это примерно то же самое, что разбираться в Талмуде.
              Ответить
    • Вот, кстати, хорошо оптимизирует. И clang, и gcc. icc только почему-то не хочет. И на армах gcc тоже не хочет.

      http://gcc.godbolt.org/#{%22version%22%3A3%2C%22filterAsm%22%3A {%22labels%22%3Atrue%2C%22directives%22% 3Atrue%2C%22commentOnly%22%3Atrue%2C%22i ntel%22%3Atrue}%2C%22compilers%22%3A[{%22sourcez%22%3A%22MQSwdgxgNgrgJgUwAQB4 DOAXO4MDoAWAfAFDE5IhoD6UIGGUCVCY2AhmABQB uA9iHACUSAN7EkEpDDAheYUeMkSAkNLQgA5mARwk EfGwBOSAEYBPDAjQBtACwBdANyKlEmDgDMAJioYk 3NlgEZyUAXyRGJABeUQAGAA9YgEYAGiQE2K80jI9 sxNtQ5xckQwQMGEN5DNtYj0zk6JjGXACg51DScko qE01mVhAOHn4hBSVpWXkxVyRVMHUtHT0DY3NLGwc QmalPHz9WmGDi8JNkGJFqvNqrrPTEpMLSJVLyyrv YmrqvBqiY05bAod2kAAA%22%2C%22compiler%22 %3A%22%2Fusr%2Fbin%2Fclang%2B%2B%22%2C%2 2options%22%3A%22-O3%20%22}]}
      Ответить
      • Блеать, вот это ссылка... Мне что-то влом вырезать из нее пробелы ;)
        Ответить
        • tr -d " \n" | xargs python -m webbrowser
          Должно помочь.
          Ответить
          • Это для прыщеблядей способ. Для смартфона есть?
            Ответить
        • В блокнот и поиском с заменой. Может, юзерскрипт для ссылок запилить?
          Ответить
          • Это для десктопоблядей способ. Для смартфона есть?
            Ответить
      • Ссылкосокращатели для кого придумали?
        Ответить
        • Для твиттерщиков.
          У меня иррациональная неприязнь к сокращалкам ссылок, даже если они, как например здесь, уместны.
          Ответить
      • >И на армах gcc тоже не хочет.
        С армами и gcc вообще интересная ситуация
        https://en.wikipedia.org/wiki/Endianness#Bi-endian_hardware

        Some architectures (including ARM versions 3 and above, PowerPC, Alpha, SPARC V9, MIPS, PA-RISC, SuperH SH-4 and IA-64) feature a setting which allows for switchable endianness in data segments, code segments or both. This feature can improve performance or simplify the logic of networking devices and software. The word bi-endian, when said of hardware, denotes the capability of the machine to compute or pass data in either endian format.

        Ну и в самом гцц есть опции для выставления режима http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html

        -mlittle-endian
        Generate code for a processor running in little-endian mode. This is the default for all standard configurations.
        -mbig-endian
        Generate code for a processor running in big-endian mode; the default is to compile code for a little-endian processor.
        -mwords-little-endian
        This option only applies when generating code for big-endian processors. Generate code for a little-endian word order but a big-endian byte order. That is, a byte order of the form ‘32107654’. Note: this option should only be used if you require compatibility with code for big-endian ARM processors generated by versions of the compiler prior to 2.8. This option is now deprecated.
        Ответить
        • > bi-endian
          И мальчиков и девочек любит? На ходу что-ли менять умеет?
          Ответить
          • ARM Architecture Reference Manual https://www.scss.tcd.ie/john.waldron/3d1/arm_arm.pdf
            A2.7 Endian support
            Ответить
      • #include <stdint.h>
        
        int is_little_endian(void) {
            union {
              	unsigned char bytes[4];
                uint32_t value;
            } le = {0x01, 0x02, 0x03, 0x04};
        
            return 0x04030201 == le.value;
        }
        
        int is_big_endian(void) {
            union {
              	unsigned char bytes[4];
                uint32_t value;
            } be = {0x04, 0x03, 0x02, 0x01};
        
            return 0x04030201 == be.value;
        }
        Ответить
        • Ответить
          • Это тот самый код, который лежит в чудовищной ссылке. Да, оптимизируется до возврата константы везде, кроме gcc на ARM да ICC (что удивительно).
            Ответить
        • Ну зачем для рантайма, сказано же на макросах!
          Ответить
          • static inline и в хедер.
            Ответить
            • Один хрен в ifdef'ах не поюзать ;)
              Ответить
              • дописать constexpr и в специализацию шаблонов же
                Ответить
                • #include <cstdint>
                  #include <cstdio>
                  
                  typedef union {unsigned char bytes[4]; uint32_t value;} lol_t;
                  
                  int constexpr is_little_endian(void) {
                    	return 0x04030201u == ((lol_t) {0x01, 0x02, 0x03, 0x04}).value;
                  }
                  
                  int constexpr is_big_endian(void) {
                  	return 0x04030201u == ((lol_t) {0x04, 0x03, 0x02, 0x01}).value;
                  }
                  
                  int main() {
                  	std::printf("%d\n", is_little_endian() ? 5555 : 8989);
                  }


                  FreeBSD clang version 3.3 вообще отказался это собирать, сказав:
                  constexpr function never produces a constant expression.
                  note: read of member 'value' of union with active member 'bytes' is not allowed in a constant expression

                  Компиляторы, которые есть в списке gcc.godbolt.org, все-таки собрали это.

                  Несмотря на наличие constexpr, ICC все равно нагенерировал сравнение: http://goo.gl/aGj8zF

                  С GCC под ARM похожая история: http://goo.gl/e9ePxW Я не очень умею читать ассемблер на ARM, но вроде все выходит так:
                  Сперва по кусочкам (!) {0x01, 0x02, 0x03, 0x04} загружается в регистр r3 (см. инструкции BFI, Bit Field Insert), затем 0x4030201 грузится за два захода в r4 через пару mov и movt (ARM, походу, иначе не умеет), и потом они сравниваются.
                  http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjagdjbf.html
                  http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cihcdbca.html
                  http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjagchif.html
                  Ответить
                  • > Несмотря на наличие constexpr, ICC все равно нагенерировал сравнение
                    Ты не поверишь, но он не обязан генерировать константу.
                    Ответить
                    • Логично. Более интересно то, что случилось, когда я попробовал использовать эту "константу" там, где нужен тру-constexpr.
                      std::array<int, is_little_endian() ? 4: 16> test;

                      ICC: constant value is not known
                      GCC под ARM тоже сломался:
                      in constexpr expansion of ‘is_little_endian()’
                      error: accessing ‘lol_t::value’ member instead of initialized ‘lol_t::bytes’ member in constant expression

                      Походу, компиляторы проверяют constexpr на ошибки только при раскрытии.

                      Edit: Только шланг молодец, сразу нашел подвох.
                      Ответить
                    • Ты должен был про consteval подсказать.
                      Ответить
                      • Как он в 2014 году мог знать про "consteval", который появился в C++20?
                        Ответить
        • А такая хуета не имеет право оптимизнуться в return true, return false?
          Ответить
          • Имеет. Ну и это даже желательно.

            Но не факт конечно что компиляторы заморачиваются с оптимизацией каста через юнион.
            Ответить
            • «clang» и «gcc» успешно оптимизируют, а вот недавно упомянутый j123123 «icc» сосёт:
              is_little_endian():
                      xor       eax, eax                                      #9.26
                      cmp       DWORD PTR le.6.0.0.1[rip], 67305985           #9.26
                      sete      al                                            #9.26
                      ret                                                     #9.26
              le.6.0.0.1:
                      .byte   1
                      .byte   2
                      .byte   3
                      .byte   4
              is_big_endian():
                      xor       eax, eax                                      #18.26
                      cmp       DWORD PTR be.8.0.0.2[rip], 67305985           #18.26
                      sete      al                                            #18.26
                      ret                                                     #18.26
              be.8.0.0.2:
                      .byte   4
                      .byte   3
                      .byte   2
                      .byte   1
              Ответить
    • - Меня избили, связали и, скорее всего, собирались убить! У меня всё тело до сих пор болит, естественно, я принял обезболивающее! И уж тем более не стоит меня попрекать тем, что я пытался сбежать, когда очнулся чёрт знает где.
      Ответить

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