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

    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
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    #include <stdio.h>
    #include <stdlib.h>
    #include <inttypes.h>
    
    typedef struct list list;
    
    struct list
    {
      list* next;
      uint32_t data;
    };
    
    #define ADD_LIST(ptr, val) \
    do { \
      (ptr)->next = (list *)alloca(sizeof(list)); \
      (ptr)->next->data = val; \
      (ptr)->next->next = NULL;\
    } while (0)
    
    // https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
    #define INIT_LIST(val) \
    ({ \
      list *INIT_LIST = (list *)alloca(sizeof(list)); \
      INIT_LIST->data = val; \
      INIT_LIST->next = NULL; \
      INIT_LIST; \
    })
    
    
    int main(void)
    {
      list *a = INIT_LIST(5);
      ADD_LIST(a,10);
      ADD_LIST(a->next,15);
      ADD_LIST(a->next->next,20);
      ADD_LIST(a->next->next->next,25);
      ADD_LIST(a->next->next->next->next,30);
    
      for(list *ptr = a; ptr != NULL; ptr = ptr->next)
      {
        printf("%d ", ptr->data);
      }
    
      return EXIT_SUCCESS;
    }

    А можно ли в крестоговне так сделать без Сишного Препроцессора?

    Запостил: j123123, 19 Июля 2021

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

    • Жаль что "alloca" это не часть стандарта.
      Ответить
      • напиши свой помпилятор... вот у меня есть
        mlir::Value newStringValue = rewriter.create<LLVM::AllocaOp>(op->getLoc(), i8PtrTy, size, true);
        Ответить
        • LLVM - говно. https://govnokod.ru/27456

          И кстати LLVM IR тоже говно. Попробуй на нем "Сетунь" описать - нихуя не выйдет, потому что в LLVM IR есть какие-то "биты", а в "Сетунь" никаких битов нет. https://govnokod.ru/23305#comment389657
          Ответить
      • Ни разу не жаль, alloca — говно.

        На VLA переписать нельзя?
        Ответить
        • На VC нет VLA потому что нет C99.
          Либо alloca, либо тогда уже кресты и std::vector
          Ответить
          • VC - говно.
            Ответить
            • На самом деле MSу просто похуй на ансишечку: они на крестах пишут, и текут
              Ответить
              • В говнокомпиляторе от MS и кресты через Ж реализованы, с неоднократным нарушением говностандарта. Впрочем, ублюдский говностандарт крестов ни один компилятор полностью не осилил реализовать.
                Ответить
                • Именно потому я за то, чтобы не выебываться, а писать на старых стандартах.
                  У меня в C++03 всё работает
                  Ответить
        • > На VLA переписать нельзя?

          VLA не позволяет ничего довыделить. Разве что рекурсией можно, но тогда и VLA нахуй не нужно.
          Ответить
          • Действительно.

            Но, кмк, кроме всех бед alloca c инлайнами её главная проблема в семантике разврата.

            В отличие от malloc, который хотя бы NULL вернёт, если память кончилась.

            alloca делает невозможным написание хотя бы подобия безопасного кода. Во-1 стек тихо переполняется, во-2 поведение при этом не определено.
            Ответить
            • А что будет если я в VLA напишу

              int petuh[get_the_biggest_number()];
              Ответить
              • что еще за "get_the_biggest_number()"? На какой это архитектуре? У тебя ведь biggest number может быть 64-битным, а указатели на твоей платформе 32-битные
                Ответить
                • > что еще за "get_the_biggest_number()"?
                  Функция возвращает какое-то очень большое число. Оно правильного типа, но стек уже переполнился. Что будет?
                  Ответить
                  • Я думаю что будет implementation и platform defined. Если ты просто такую хуйню где-то напишешь, и не засрешь те элементы массива, которые переполяют стек, ничего плохого может не произойти. Хотя компилятор может и навставлять хуйни, которая чекает доступное пространство на стеке, и будет падение.

                    Если ты потом еще что-то навыделяешь на стеке даже без VLA, оно может разместиться после той хуйни, и если ты туда что-то поназаписываешь, будет падение. Хотя можно и так напереполнять, что можно дотянуться до хипа, и тогда можно туда какой-то хуйни насрать, обойдя guard page
                    Ответить
                    • Ну то есть проблема alloca есть у VLA: нельзя бездумно выделить сколько угодно памяти на стеке не предприняв каких-то защитных действий.

                      А в куче можно на нул проверить

                      >. Хотя можно и так напереполнять, что можно дотянуться до хипа,

                      На x64 это наверное надо очень наглым быть
                      Ответить
            • > alloca делает невозможным написание хотя бы подобия безопасного кода. Во-1 стек тихо переполняется, во-2 поведение при этом не определено.

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

                В том же жс есть исключение про переполнение стека, которое можно поймать и обработать.
                Но нет исключения про переполнение кучи, поэтому new просто убивает программу.
                Ответить
              • Рекурсивные алгоритмы стараюсь не использовать (я же не хуцкелист).

                В таком случае максимальная глубина стека статически вычисляется при сборке программы.

                То есть строго фиксирована не зависит от пользовательских данных, пришедших в scanf.
                Таким образом введя хуеву тучу данных ошибку нехватки памяти можно корректно обработать и программу нельзя внезапно закрашить с buffer overflow.
                Ответить
                • > Таким образом введя хуеву тучу данных ошибку нехватки памяти можно корректно обработать и программу нельзя внезапно закрашить с buffer overflow.

                  Это можно и в случае с выделением на стеке сделать, обработав соответствующий сигнал, когда память пытается читаться и/или записываться в "guard page" https://dave.cheney.net/wp-content/uploads/2015/08/threads.png
                  Ответить
                  • > guard page
                    Это всего лишь иллюзия безопасности. Выделяешь большой массив через VLA, пишешь ему в конец — всё, някаких "guard page" там нят, всё сломалось.

                    Об этом был как-то большой разговор ня ГК.
                    Ответить
                    • > Это всего лишь иллюзия безопасности. Выделяешь большой массив через VLA, пишешь ему в конец — всё, някаких "guard page" там нят, всё сломалось.

                      Это вполне решается "протыкиванием" памяти перед первым реальным обращением в VLA. Ну и если в рантайме программе известно, в каком адресе стек кончается - можно это тупо проверять.

                      И никакого VLA тут не нужно. Такая же питушня будет с обычным выделенным на стеке массива большого размера
                      Ответить
                      • Выделяем гигабайт — протыкиваем 16 раз. Какой перфоманс )))

                        Уж лучше тогда реальня проверять границу стека, это хотя бы одно сравнение.
                        Ответить
                        • Часто на стеке гигабайт выделяешь?
                          Ответить
                          • Это worst-case. Best-case пробинга будет при выделении меньше страницы, и даже в этом случае оверхед будет примерня равен сравнению с границей.
                            Собствення, выделение 65 килобат — вполне себе обычняя операция, и уже в этом случае пробинг нячинает сливать.
                            Ответить
                            • Замечу, что вообще-то никто не мешает под guard page выделять более одной страницы. Особенно на x86-64
                              Ответить
                              • The point stands.
                                Ответить
                                • Кстати, если посмотреть на реализацию виртуальной памяти в процессорах на архитектуре x86-64
                                  https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details
                                  То адреса в диапазоне от
                                  00 00 80 00 00 00 00 00
                                  до
                                  ff ff 7f ff ff ff ff ff

                                  являются на данный момент (Current 48-bit implementation) невалидными на всех существующих процессорах с архитектурой x86-64, и это можно использовать как "guard page", потому что там просто нихуя нет.
                                  Ответить
                                  • Было бы удобно, если бы адреса были в середине адресного пространства

                                    С одной стороны стек, с другой -- куча, а по середине естественная граница, а?
                                    Ответить
                                    • А чем это принципиально лучше того, что сейчас? Граница - переход 0000... ffff... .

                                      Ну и вся эта идиллия нахрен ломается, если у нас несколько процессов на одном адресном пространстве, и у каждого свой стек в нем.
                                      Ответить
                            • Собстве́ння

                              Существительное, неодушевлённое, средний род. Происходит от гл. собствети, далее из праслав. *sobstveti «някять; понякивать»

                              (Вяки-слонярь)
                              Ответить
                • Ну и формально никто не мешает перед alloca() и перед обычными локальными переменными (или даже перед вызовом функции, если нам известно, сколько там байт под локальные переменные поюзается) смотреть, сколько там еще осталось до конца стека, и если стек уже всё, можешь что-то там обрабатывать. Но это оверхедно
                  Ответить
    • Переписал тебе код, проверь:
      { \
        (ptr)->next = (list *)alloca(sizeof(list)); \
        (ptr)->next->data = val; \
        (ptr)->next->next = NULL;\
      }
      Ответить
      • Предлагаешь это копипастить?
        Ответить
        • Чем копипаст этого блока отличается от копипаста того цикла?
          Ответить
          • Какого "того цикла"?
            Ответить
            • У которого только одна итерация.
              Ответить
              • Этот цикл там не просто так: https://stackoverflow.com/questions/154136/
                Ответить
                • Классика, чтобы не думать и течь, используя вызов макроса как вызов функции в ветвях условного оператора без фигурных скобок.

                  Кстати, есть пример, когда do { ... } while(0) имеет смысл в обычном коде: break или continue досрочно завершает выполнение блока, и не нужно лепить goto или косяки вложенных ифов.
                  Ответить
                • В данной программе можно обойтись и обычным «{}». У тебя «overengineering», проверть.
                  Ответить
                  • Заповеди макроёбства не просто так придуманы. Без do while точка с запятой лишней будет.
                    Ответить
                    • Второй мокрос же не обёрнутый, это несправедливо.
                      Ответить
                      • Второй макрос косплеит expression а не statement.
                        Ответить
                        • if (true) 
                              l = INIT_LIST(5);
                          else
                             std::cout << "Kakoj bagor (((";
                          Ответить
                          • А где багор?
                            Ответить
                            • На стаковерфлоу написано, что будет багор, т.к. else отвяжется от if.

                              А, нет, это про другое написано.

                              > It doesn't help to wrap things in curly braces within the macro, because a semicolon after the braces is syntactically incorrect.

                              Решение проблемы довольно просто́: не ставить «семиколон» после мокроса.
                              Ответить
                              • Ну и сиди потом думай где ставить, где не ставить...

                                Именно поэтому я за do while.

                                Но, повторюсь, в твоём примере expression а не statement.
                                Ответить
                                • > Ну и сиди потом думай где ставить, где не ставить

                                  Это, кстати, даже к лучшему: вот захочешь ты взять указатель на функцию... А это мокрос. Да и правило простое: после мокроса скмиколон не ставится.
                                  Ответить
                            • #define INIT_LIST(x) x;if(false) {}
                              Ответить
                              • Блин ну вы о чём?

                                Второй макрос же в ({...}) завернут. Все норм с ним, просто гнутое расширение требует.
                                Ответить
                                • > ({...})

                                  Оператор post-goatse.

                                  А разве он не в «{}» завёрнут? Или мокрос кончается на первой попавшейся пустой линии?

                                  > Линии

                                  Ну вот, скоро начну и «аппликации» говорить (((
                                  Ответить
                                  • Ну там же вон круглые скобочки вокруг.
                                    Ответить
                                    • Я думал, это такой синтаксис у мокроса:

                                      #define KOKO (body)
                                      Ответить
                                      • А как различить их, кстати? body аргумент и body тело.
                                        Ответить
                                        • Х.з., видимо это гнутое расширение не работает на макросах без аргументов.
                                          Ответить
                                          • Я подозреваю, что макрос ближайшие скобочки всегда считает своим аргументом, а на тело отводится то, что осталось (вне зависимости от гну/негну). Т. е. макрос со скобочками в теле, но без аргументов в принципе объявить нельзя.
                                            Ответить
                                            • Да нет, работает. Отличает по пробелу между именем макроса и круглой скобочкой, судя по всему. Если пробела нету -- аргументы. Если есть -- значит просто тело со скобочками.
                                              Ответить
                                              • Точно! Забыл про пробел в макросе. Я на это попадался...

                                                Привык, что сишке/крестам плевать на пробелы.
                                                Ответить
                                                • А вот в «Nim» пробелы очень важны. Как вы думаете, что выведет этот код?

                                                  echo 2+2 * 3+3
                                                  Ответить
                                                  • Не знаю, но есть подозрение, что у пробелов низкий приоритет, т. е. он посчитает это как
                                                    echo (2+2) * (3+3)

                                                    Так?

                                                    А звёздочка будет означать по-прежнему умножение или что-то другое?
                                                    Ответить
                                                    • (display (* (+ 2 2) (+ 3 3)))
                                                      Ответить
                                                    • Вовсе нет, ответ: 11.

                                                      А что выведет такой код, угадаешь?
                                                      proc `=?=`(a, b: int): int =
                                                        result = a * b 
                                                      
                                                      echo (2 =?=3=?= 4)
                                                      
                                                      echo (2 =?= 3 =?= 4)
                                                      Ответить
                                                      • > ответ 11

                                                        Ну блин, скучно. Везде ведь так.
                                                        Ответить
                                                        • Я думал, что будет немного другой ответ. Просто я ещё не очень хорошо выучил «Nim», но он мне всё равно очень нравится :-)
                                                          Ответить
      • Сразу видно, что ты математик. Раскрыть препроцессор - задача формально решена.
        Ответить
      • зачем писать код который ни один анализатор не возмет... это просто выстрелить себе в ногу из дробовика
        Ответить
        • Именно поэтому я за «PHP».
          Ответить
        • Зачем мне подстраивать свой код под всякие заедушные анализаторы? Если какой-то там анализатор чего-то не понимает в моем коде, то это проблема анализатора
          Ответить
    • Надо было в цикле ноды создавать, имхо, тогда идея лучше бы раскрылась. А то сейчас можно и без alloca() на обычных локалках переписать.
      Ответить
      • Только надо было бы придумывать каждой локалке уникальное имя. А еще локалки дохнут при выходе из скоупа, а выделенная через alloca() хрень дохнет только при выходе из функции.
        Ответить
        • Хотя нафиг локалки то, std::array да и всё. Или что-то своё похожее налепить. Для статичных структур нету смысла городить костыли через alloca().

          Всё-таки тут надо было именно динамичный пример показать, что ты создаёшь ноды на лету и не знаешь о них ничего заранее.
          Ответить
          • Например так https://wandbox.org/permlink/NgPO273UEQY5ncfd ?

            Или надо чтобы что-то из stdin считывалось и в такой список упихивалось?
            Ответить
            • Да, как-то так.
              Ответить
              • https://wandbox.org/permlink/QSe51o1Mt6kuuBNg вот, улучшил чтоб с scanf(). Немного поменял макросы.

                #define ADD_LIST(ptr, val) \
                do { \
                  (ptr)->data = val; \
                  (ptr)->next = (list *)alloca(sizeof(list)); \
                  (ptr)->next->next = NULL;\
                } while (0)
                
                // https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
                #define INIT_LIST() \
                ({ \
                  list *INIT_LIST = (list *)alloca(sizeof(list)); \
                  INIT_LIST->next = NULL; \
                  INIT_LIST; \
                })


                Теперь список с нулем элементов это не когда указатель на такой список равен NULL, а когда ->next равен NULL. И INIT_LIST() теперь нихрена не принимает в качестве первого значения
                Ответить
                • Ну вот теперь официально можно заявлять что крестобляди соснули т.к. alloca() нельзя вернуть из вложенных функций.
                  Ответить
                  • надо редактировать фрейм поинтеры... - причем срочно
                    Ответить
                    • У меня не получилось, надо генератор эпилога в самом конпеляторе править. Иначе остаётся тонкий момент в который может ебануть прерывание сигнал и всё испортить.
                      Ответить
                • ого, динамическая структура на стеке это круто;) Почти как троллейбус из булки
                  Ответить
                  • Не забывай, что в контроллере у меня может не быть никакого хипа.
                    Ответить
                    • Да и стека у тебя кот наплакал.
                      Ответить
                    • Может в стат память тогда лучше?
                      Ответить
                      • В стат. памяти ты должен заранее знать, сколько там байтиков надо. А тут их хуй знает сколько надо.
                        Ответить
                        • Почему не выделить сразу по максимуму?
                          Ты ведь тоже можешь стековерфлоу схватить
                          Ответить
                          • "по максимуму" это сколько?
                            Ответить
                            • Вся статическая память минус иницилизорванная минус BSS, не?
                              Ответить
                              • Что еще за "инициализированная"? Что за "BSS"? Инициализированная в RAM или в flash? В разных контроллерах у тебя flash бывает и в одном адресном пространстве с RAM, и в разных. В RAM памяти у тебя при включении обычно будет всякий рандом, и кстати его можно использовать как seed в ГПСЧ каком-нибудь.

                                https://hackaday.com/2015/06/29/true-random-number-generator-for-a-true-hacker/
                                > When power is applied to the MCU, its internal volatile RAM has unknown contents. Each flip-flop will be preset to a 0 or 1 state – a consequence of the imperfection of internal circuits, power supply glitches, surrounding current flow, or thermal (or even quantum) noise. That’s why the content of RAM is different each time it is powered on.

                                Так что для BSS тебе нужен код зануления, а дня инициализированной (если это RAM) тебе нужен код, который из флеша перекопирует в RAM.
                                Ответить
                                • какая-то сомнительная идея
                                  его вздрючат
                                  Ответить
                                • Ого, так ты сам себе загрузчик: обычно же это именно он копирует дату и зануляет BSS:)

                                  Как разделяется адресное пространство с точки зрения сей, кстати?
                                  Ответить
                                  • > Как разделяется адресное пространство с точки зрения сей, кстати?

                                    Это implementation и platform dependent хуйня. Компиляторы могут всякие там директивы поддерживать. GCC и keil например имеет какие-то __attribute__((section("name")))
                                    https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html
                                    https://www.keil.com/support/man/docs/armcc/armcc_chr1359124982450.htm

                                    В AVR у GCC еще есть https://gcc.gnu.org/onlinedocs/gcc/AVR-Variable-Attributes.html такая хуйня, ведь там адресное пространство под флеш и адресное пространство под оперативку разное

                                    Под линуксом можно в объектные файлы насрать всяких разных секций и потом через линкер-скрипт это как-то там собирать можно https://users.informatik.haw-hamburg.de/~krabat/FH-Labor/gnupro/5_GNUPro_Utilities/c_Using_LD/ldLinker_scripts.html
                                    Ответить
                                    • Забавно, малость напоминает ближние/дальние/хьюдж указатели в досе)
                                      Ответить
                              • Кстати вот в контроллерах FX2 и FX2LP никакого встроенного EEPROM вообще нет. https://www.cypress.com/file/138911/download
                                During the power-up sequence, internal logic checks the I2C port
                                for the connection of an EEPROM whose first byte is either 0xC0
                                or 0xC2. If found, it uses the VID/PID/DID values in the EEPROM
                                in place of the internally stored values (0xC0), or it boot-loads the
                                EEPROM contents into internal RAM (0xC2). If no EEPROM is
                                detected, FX2LP enumerates using internally stored descriptors.
                                The  default  ID  values  for  FX2LP  are  VID/PID/DID  (0x04B4,
                                0x8613, 0xAxxx where xxx = Chip revision)

                                Есть всякие дешевые лог. анализаторы на базе этого контроллера, и там прошивка в оперативку заливается по USB при каждом включении.
                                Ответить
                  • Иди няхуй) А вообще, на всех обижаться - нервов не хватит.
                    Ответить
    • Нидзес, ты здесb?
      Ответить

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