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

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    #include <stdio.h>
    
    struct Gost {
       int x = 42;    
    };
    
    
    int main () {
        Gost gst;
        printf("%d\n", gst); // 42
    }

    http://ideone.com/fB26cs

    Уб ли это?

    Запостил: OCETuHCKuu_nemyx, 06 Октября 2019

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

    • 6.7.2.1-13. A pointer to a structure object, suitably converted, points to its initial member

      Как это работает с значениями а не указателями не знаю
      Ответить
    • Хороший вопрос. В известных реализациях к неупакованным структурам мусор для выравнивания размера («паддинг») обычно добавляется в конце, так что указатель на структуру совпадает с указателем на её первое поле.
      Ответить
    • UB кмк.

      Никто не гарантирует тебе паддинг сзаду
      Ответить
      • > There may therefore be unnamed holes within a structure object, but not at its beginning, as necessary to achieve the appropriate alignment.

        http://port70.net/~nsz/c/c89/c89-draft.html#3.5.2.1
        Ответить
        • аа
          ну тогда не убэ

          Питух, а почему ты все еще петух, когда мы все уже давно обезьяны?
          Ответить
          • На самом деле не факт: конпелятор структурку мог, например, до восьми байт разуплотнить. Нужно курить стандарт на тему передачи структур в vararg функции и работу «%d».
            Ответить
            • ну допишет в конце еще 4 байта нулевых, и чо?
              хотя... может быть все будет зависеть от байтордера
              Ответить
              • Так мы обсуждаем потенциальные UB. На практике, скорее всего, всё будет работать прекрасно, а вот что говорит об этом Стандарт — совсем другое дело…
                Ответить
                • В итоге чем закончилось расследование Стандарта?
                  Ответить
                  • не знаю, я тупая обезьяна
                    мне надо удалять слово onclick во всех переменных моего сайта, иначе будет XSS

                    не до стандартов
                    Ответить
                    • Не забудь ещё удалить слово «DROP», а то будет Иссык-Куль-инъекция.
                      Ответить
                  • Отдел расследований сильно занят, запрос в очереди висит.
                    Ответить
              • От байтордера зависит интерпретация чисел, которые не помещаются в один байт. Вроде больше он ни на что не влияет.

                Передача параметров в функции — вообще отдельная тема. При передаче через стек компилятор аргументы функции разуплотняет, поэтому, например, на x86 нельзя передать char, он будет разуплотнён до мышиного слова (да, в документации по vararg это описано). Это не противоречит строке формата printf: "%d" как раз ожидает мышиное слово.
                Ответить
                • показать все, что скрытоО том, почему "призрак коммунизма" побродив по Европе, осел в России написано много, как самими коммунистами, так и их "разоблачителями". Марксистское лжеучение было подхвачено значительной частью "не у дел стоящих" евреев того времени.

                  В ней были все необходимое для провоцирования революционной смуты. Была и жесткая критика общественных отношений, основанных на эксплуатации (изобличать может не только праведник, но и плут), и необходимость переустройства общества на основе СПРАВЕДЛИВОСТИ (мечта простых людей), и наукообразное обоснование всей этой ахинеи и обещание земного рая в светлом будущем. Незаконченность теории, системная незавершенность, эклектизм позволяли удачно пришедшим к власти ее приверженцам "творчески развивать и дополнять" новую религию в зависимости от времени, места и ситуации.

                  Ортодоксальные марксисты считали маловероятной возможность пролетарской революции в России, в стране, где этот самый пролетариат не составлял и 1% населения. Да и сознательность российского пролетариата, который пополнялся за счет "бомжующих" крестьян, особого энтузиазма не вызывала.
                  Ответить
          • Я пошел по другой ветке эволюции. Мне лень регать новую факйу.

            У меня есть обезьянья файка еще с дообезяньей епохи, но я не помню от нее пороль.
            Ответить
          • А я вообще собака страшная, гав-гав!
            Ответить
    • https://wandbox.org/permlink/260AcuqcX8qRtfFa
      #include <stdio.h>
      
      struct Gost {
         double d = 3.14;
         int x = 42;
      };
      
      static_assert(sizeof(double) > sizeof(int));
      
      
      int main () {
          Gost gst;
          printf("%d\n", gst); // still 42 (gcc, clang)
      }
      Ответить
      • показать все, что скрытоvanished
        Ответить
      • Результат зависит от ABI. В зависимости от атрибутов функции printf в данной библиотеке может выводить либо 42, либо несколько первых байтов внутреннего представления 3.14. Хорошо, что в виндах на x86-64 оставили один стандартный ABI (ms_abi) и в *никсах на x86-64 тоже один стандартный ABI (sysv_abi). А вот на других процессорах целый зоопарк соглашений вызова.

        Почему 42? Когда вызывающий код пушит gst, на выбранной платформе он сначала пушит gst.d в стек плавающего питуха, а потом заносит gst.x в регистр. Функция printf ожидает значение "%d" в регистре, а значение "%f" в стеке плавающего питуха. Чтобы функция printf выдавала ожидаемый результат, нужно, чтобы кобенация "%f" и "%d" в первом аргументе совпадала с кобенацией плавающих и целых питухов в последующих аргументах.

        А на других платформах по умолчанию сишка использует cdecl. В 32-битном коде для x86 cdecl использует стек плавающего питуха только для возврата значений, а при передаче аргументов всё пушится в стек. Поэтому такой код выведет 1374389535.
        Ответить
        • показать все, что скрыто@Горилла
          @Результат зависит от ABI. В зависимости от атрибутов функции printf[...]

          А я и не предполагал, что между сишниками и паскалистами столь значительная разница.
          Ответить
          • В реализациях Паскаля тоже можно использовать разные соглашения вызова:
            https://freepascal.org/docs-html/current/prog/progse22.html
            https://freepascal.org/docs-html/current/ref/refsu71.html

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

            В стандартном Паскале вообще не было функций с вариабельным списком аргументов.

            Write, Writeln — это хак. Компилятор разбивает Write(x, y, z) на отдельные вызовы Write(x); Write(y); Write(z). В «Турбо Паскале» для компиляции модуля SYSTEM.PAS был специальный бутстрап-файл SYSTEM.TPS, в котором перечислены все подобные хаки.

            array of const в «Delphi» — это более безопасная конструкция, чем свободный список параметров у printf. array of const пушит общее количество элементов массива, тип и размер каждого элемента. Функция с аргументом типа array of const точно знает, что ей передали.

            Но сишники считают, что array of const — это оверхед, потому что в функцию передаётся много лишнего (а сишная строка "%d%f" — это не лишнее, ага).
            Ответить
            • показать все, что скрытоТем лучше. Нехуй писать на делфе консольные проги.
              К слову, при открытии, делфи сразу создаёт пустую форму, будто приглашая писать гуй. Это разумно и естественно.
              Так каким же нужно быть обдолбком, чтобы закрыть проект, выбрать меню и перейти New->Other->konsole application?..
              На мой взгляд, это атавизм.
              Ответить
              • Как призыв к холивару пойдёт. Но он не принят. Проблема, поднятая в данной ветке, вообще никак не связана с визуальным представлением (консоль, GUI). Вывод может быть и в файл (fprintf, Write с первым аргументом файлового типа), и в другую строку (sprintf, SysUtils.Format), и ещё куда-нибудь.

                К слову, полезная программа может вообще не иметь ни окна, ни консоли, а общаться с другими программами по специальным каналам. В Windows такие программы называют службами, в Линуксе — демонами.
                Ответить
                • показать все, что скрытоvanished
                  Ответить
                • показать все, что скрытоЭто устаревшая практика и ей место на свалке. Если в нормальной современной энтерпрайзной программе встречается говноюзанье обёрток, вроде fprintf или write, этой проге место в помойке, а ее автору - в аду.

                  @Вывод может быть и в файл
                  В этом случае гораздо разумнее реализовать это с использованием API текущей операционки. Хватит писать совместимый (юзающий только stdlib), но нахуй никому не нужный код.
                  Ответить
                  • От sprintf и SysUtils.Format тоже откажемся и будем каждый раз вручную писать спагетти, когда нужно что-нибудь отформатировать?
                    Ответить
                    • показать все, что скрытоFormat - очень опасная, и дорогая в плане ресурсоемкости функция, способная обрушить приложение.

                      Жаль, скринов не сохранилось, а то бы я показал, что вытворяет кривой format в проге на работе ("Квазар", C#).
                      Ответить
                      • Допустим, что на разбор строки формата требуются ресурсы, и в критичных к быстродействию приложениях это важно, поэтому лучше использовать более специализированные функции.

                        А почему обёртки над файловыми операциями — это плохо? При использовании обёрток одну и ту же программу можно (с незначительными доработками, а то и вовсе без них) скомпилировать под разные операционные системы. При появлении очередной системы новый код один раз дописывается в стандартную библиотеку. Если же мы откажемся от обёрток, нам придётся каждый раз полностью переписывать все наши программы.
                        Ответить
              • показать все, что скрыто> konsole
                Сразу под KDE
                Ответить
            • показать все, что скрытоhttps://progse.cx/

              А вообще хорошо, когда есть только Win32 ABI и можно спокойно шлепать формы.
              Ответить
        • Это бы выясняло. Теперь следующая головоломка: https://wandbox.org/permlink/XtEd6PDhEiE1fvH9
          struct Gost {
             float d = 3.14;
             int x = 42;
          };
          
          static_assert(sizeof(float) == sizeof(int));
          
          
          int main () {
              Gost gst;
              printf("%d\n", gst); // 1078523331 (gcc, clang)
          }
          Ответить
          • Кстати, %f ожидает double, есичо.
            Ответить
          • А что не так? надо знать внутренее представление плвающей тучки и выровнивание стурктуры
            Ответить
            • Ну, float должен был улететь в стек плавающего петуха, а int -- в регистр. Т.е. согласно тезису господина Гориллы, на x86 (по крайней мере) вывело бы 42, а не внутреннее представление float.
              Ответить
              • показать все, что скрытоvanished
                Ответить
              • Ладно, исследовательский отдел начал исследование Стандарта C. На данный момент выяснено, что описываемый код — UB:
                6.5.2.2 Function calls
                §6
                If the expression that denotes the called function has a type that does not include a prototype, the
                integer promotions are performed on each argument, and arguments that have type float are
                promoted to double. These are called the default argument promotions. [...]

                7.21.6.1 The fprintf function
                §3
                [...]
                The format is composed of zero or more directives: ordinary multibyte characters (not %), which
                are copied unchanged to the output stream; and conversion specifications, each of which results
                in fetching zero or more subsequent arguments, converting them, if applicable, according to the
                corresponding conversion specifier, and then writing the result to the output stream.

                7.21.6.1 The fprintf function
                §8
                The conversion specifiers and their meanings are:
                d,i The int argument is converted to signed decimal in the style [-]dddd. [...]

                7.21.6.1 The fprintf function
                §9
                If a conversion specification is invalid, the behavior is undefined.297) If any argument is not the
                correct type for the corresponding conversion specification, the behavior is undefined.


                Далее исследовательский отдел приступает к углублённому анализу Стандарта и конвенций вызова. Продолжение ждите в следующей серии.
                Ответить
              • Испытательный полигон для x86-32:
                #include <stdio.h>
                #include <cstdarg>
                
                struct Gost {
                   float d = 3.14;
                   int x = 42;
                };
                
                static_assert(sizeof(float) == sizeof(int));
                
                #define DECLARE_FUNC(convention) void __attribute__((convention)) my_##convention(const char * message, ...) { \
                    va_list args; \
                    va_start(args, message); \
                    Gost tmp = va_arg(args, Gost); \
                    printf(message, tmp); \
                    va_end(args); \
                }
                
                DECLARE_FUNC(cdecl)
                DECLARE_FUNC(fastcall)
                DECLARE_FUNC(stdcall)
                DECLARE_FUNC(ms_abi)
                DECLARE_FUNC(sysv_abi)
                
                #define CALL_FUNC(convention) do {my_##convention("Hello from " #convention "! %d\n", gst);}while(0)
                
                int main () {
                    Gost gst;
                    CALL_FUNC(cdecl);
                    CALL_FUNC(fastcall);
                    CALL_FUNC(stdcall);
                    CALL_FUNC(ms_abi);
                    CALL_FUNC(sysv_abi);
                    return 0;
                }
                Это для «gcc». В других компиляторах можно было бы попробовать другие calling convention. Для этих пяти у меня получились одинаковые результаты.

                Итак, такая программа выводит 1078523331
                Для struct Gost {double d = 3.14; int x = 42;}; выводит 1374389535
                Для struct Gost {int x = 42; float d = 3.14;}; выводит 42
                Для struct Gost {int x = 42; double d = 3.14;}; выводит 42

                Теперь меняем "%d" на "%f".
                Для struct Gost {float d = 3.14; int x = 42;}; выводит ноль
                Для struct Gost {double d = 3.14; int x = 42;}; выводит 3.14
                Для struct Gost {int x = 42; float d = 3.14;}; выводит 49.92
                Для struct Gost {int x = 42; double d = 3.14;}; выводит ноль
                Ответить
                • В «x64» структурка просто копируется на стек с соответствующими последствиями:
                  struct Gost {
                     int x = 42;
                     float d = 3.14;
                     int arr[5] = {1,2,3,4,5};
                  };
                  
                  static_assert(sizeof(float) == sizeof(int));
                  
                  int f(struct Gost gost)
                  {
                      return gost.x;
                  }
                  
                  int main () {
                      Gost gost;
                      return f(gost);
                  }

                  ->
                  f(Gost):
                          push    rbp
                          mov     rbp, rsp
                          mov     eax, DWORD PTR [rbp+16]
                          pop     rbp
                          ret
                  main:
                          push    rbp
                          mov     rbp, rsp
                          push    rbx
                          sub     rsp, 32
                          mov     DWORD PTR [rbp-40], 42
                          movss   xmm0, DWORD PTR .LC0[rip]
                          movss   DWORD PTR [rbp-36], xmm0
                          mov     DWORD PTR [rbp-32], 1
                          mov     DWORD PTR [rbp-28], 2
                          mov     DWORD PTR [rbp-24], 3
                          mov     DWORD PTR [rbp-20], 4
                          mov     DWORD PTR [rbp-16], 5
                          sub     rsp, 32
                          mov     rax, rsp
                          mov     rcx, QWORD PTR [rbp-40]
                          mov     rbx, QWORD PTR [rbp-32]
                          mov     QWORD PTR [rax], rcx
                          mov     QWORD PTR [rax+8], rbx
                          mov     rdx, QWORD PTR [rbp-24]
                          mov     QWORD PTR [rax+16], rdx
                          mov     edx, DWORD PTR [rbp-16]
                          mov     DWORD PTR [rax+24], edx
                          call    f(Gost)
                          add     rsp, 32
                          nop
                          mov     rbx, QWORD PTR [rbp-8]
                          leave
                          ret
                  .LC0:
                          .long   1078523331
                  Ответить
                  • А если передавать не структурку, а отдельные параметры, всё будет сложнее.
                    Ответить
              • На x86-32 в стандартных calling conventions стек плавающего петуха используется только для возврата значения из функции, т. е. если тип результата функции float или double. Аргументы же передаются через обычный стек или через обычные регистры.

                Вот в x86-64 (amd64) всё сложнее.
                Ответить
              • показать все, что скрытоУ тебя не флоат, а варяг.
                Ответить
          • 1078523331 — это 1000000010010001111010111000011 в двоичной.
            Берём описание формата (https://en.wikipedia.org/wiki/Single-precision_floating-point_format) и парсим:
            0 | 10000000 | 10010001111010111000011
            sign = 0
            exponent = 0b10000000 - 127 = 1
            fraction = 10010001111010111000011, с ведущим битом: 110010001111010111000011
            Переводим на «десятичную систему счисления» по формуле из «Википедии»:
            b = list(map(int, '110010001111010111000011'))
            s = 0
            for i in range(1, 24):
                s += b[i] * 2**(-i)
                
            >>> 2 * (1 + s)
            3.140000104904175
            Ответить
          • показать все, что скрытоТак выяснили же уже, оно находит %d и делает стасик_катс к int32
            Ответить
    • https://ideone.com/DfZmV6
      Передавать в `printf` "неожиданный" тип - это примерно то же самое, что и ub.
      Ответить
    • показать все, что скрытоВыборы - это когда граждане думают, кого они выбирают.
      Выборы в России - это когда граждане думают, что они выбирают.
      Ответить
    • показать все, что скрыто9 октября 1989 г. на Центральном ТВ СССР состоялся 1-й телесеанс Кашпировского. Граждане самой читающей страны с лучшим в мире образованием дружно бросились к телеэкранам "получать установку". Спустя 30 лет все они так же дружно поверят в "распятого мальчика" и "фашизм в Украине"
      Ответить

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