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

    +3

    1. 1
    2. 2
    #define add(x, y) &((void*)x)[y];
    #define mul(x, y) (sizeof (char[x][y]))

    из свитера

    Запостил: 3.14159265, 10 Сентября 2020

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

    • Откуда?
      Ответить
    • add(0,0)
      нихуя не понел
      а
      сначала не понел, потом понел
      Ответить
      • А я до сих пор не понял
        Ответить
        • Теперь понял.
          Ответить
        • хинт: адрисняя арифметика
          Ответить
          • Зачем ты слово «дрисня» окружил какими-то лишними буквами?
            Ответить
            • "адрисняя" отвечает на вопрос "что делая?"
              Ответить
              • Закончив кидать, он напился воды,
                Воды одриснённой, нечистой.
                С лица его падал пот, сажи следы,
                Услышал он речь машиниста.
                Ответить
      • >нихуя не понел

        https://godbolt.org/z/YaKK8P
        Ответить
      • Боишься NullPointerException?

        Тут же нет разыменования: мы берём & от данных, а с самими данными ничего не делаем.
        Ответить
        • не боюся, уже понел, что это только про адреса
          а чтобы я еще лучше понял, мне пи lea показал
          Ответить
    • Поясни что это за хуета, Пи.
      Ответить
      • Единственная полезная структура данных — массив.
        Ответить
    • Погромист, который видел инструкцию LEA у «x86», даже не удивится.
      Ответить
    • Продолжим изучать ценность структуры «массив»
      Ответить
      • Ничего не напутано? Ложному условию соответствует нулевое смещение массива, истинному — смещение 1. Либо убрать один восклицательный знак, либо поместать менями true и false.
        Ответить
        • Да. Напутано.
          Ответить
        • https://ideone.com/7ZUAU4
          #define ternary(cond, true, false) ( (void *[]){true,false}[!cond] )
          Ответить
          • В «крестах» не конпелируется, потому что true и false заняты под ключевые слова.
            Ответить
            • >В «крестах» не конпелируется
              Так сказано, будто это что-то плохое.

              Кстати первая версия и в сишке не компилилась.

              Я второпях забыл что в compound literals нужно ставить скобки вокруг типа.
              Ответить
              • Оказывается, не из-за true и false. В крестах более строгая проверка типов. Обойти её с помощью гнутого расширения typeof мне пока тоже не удалось.
                Ответить
            • Он специально так назвал, чтобы выебнуться.
              Ответить
    • А pow слабо?
      Ответить
      • Меня не меньше интересует, можно ли реализовать деление.
        Ответить
        • Да, и не столько деление, а тьюринг-полные вещи вообще.
          if уже есть.

          Деление можно будет и скобенировать.
          Ответить
        • Вроде недавно писали, что можно умножить и прибавить по модулю 2^32.
          Ответить
          • Это я писал.

            Мысль хорошая. Но там ещё финальный сдвиг вправо нужен.
            Ответить
          • https://ideone.com/sNzX8P

            printf("%d",(6L*1431655766L)>>32); //деление на 3

            Только это на константу.
            Для пельменных так не получится.
            Ответить
        • Пытался сделать деление на 4.

          Пока что не могу откастить массив int32_t в массив char
          union {
          	int32_t ints[500];
          	char bytes[];
          } addr;
          
          	printf ("%d",(&addr.bytes)[1] - addr.bytes);


          Я уже в struct их пихал, чтобы сделать flexible array member.
          Ответить
      • >А pow слабо?
        Думаю через вложенные макросы и массивы массивов массивов это реализуемо.
        Дерзай.
        Ответить
    • #define add(x, y) sizeof(struct{char X[x], Y[y]})
      Ответить
    • #include <stdio.h>
      
      struct Array
      {
          int a;
          char bytes[];
      }; 
      
      typedef struct Array Array;
      
      union {
          int ints[500];
          Array x;
      } addr;
      
      int main(void) {
      //  printf("%d\n",(sizeof(char[x][21846])) >> 16);  // x/3
          
          printf ("%d",(&addr.x.bytes)[1] - addr.x.bytes);
          return 0;
      }
      
      bagor.c:20:30: error: invalid use of flexible array member
        printf ("%d",(&addr.x.bytes)[1] - addr.x.bytes);
                                    ^
      Ответить
    • Ура!
      Делит!

      https://ideone.com/sNzX8P

      #include <stdio.h>
      
      int main(void) {
      	char bytes22[22];
      	printf ("22 / 4 = %d\n",(int *)(&bytes22)[1] - (int *)bytes22);
      	char bytes45[45];
      	printf ("45 / 4 = %d\n",(int *)(&bytes45)[1] - (int *)bytes45);
      	
      	return 0;
      }
      Ответить
      • Сделай деление на 2, и в макрос запихни. И чтобы без минуса.

        Я сейчас пытаюсь по модулю 2 реализовать через структуру, [0...100]={{0,1}}, и каст с чар
        не получаеццо (((
        Ответить
        • Я могу сделать деление на любое натуральное число.
          И отрицательные тоже, лол.
          Ответить
    • Деление любых чисел.

      int div(b,a)
      {
          typedef struct {char lalk[a];} DIVISOR;
          char bytes[b];
          return (DIVISOR*)(&bytes)[1] - (DIVISOR*)bytes;
      }


      https://ideone.com/F5CPSJ

      Асемблерный выхлоп чист как слезинка лалки:
      div:
              movsx   rax, edi
              movsx   rsi, esi
              cqo
              idiv    rsi
              ret
      Ответить
      • Скилл.
        Я чото не могу распарить, пойду посплю.
        Ответить
        • >чото не могу распарить
          Упростил до невозможности ценой UB.
          div(a,n)
          {
              struct {char o[n];} *x=NULL, *y=a;
              return x-y;
          }
          When two pointers are subtracted, both must point to elements of the same array object or just one past the last element of the array object (C Standard, 6.5. 6 [ISO/IEC 9899:2011]); the result is the difference of the subscripts of the two array elements. Otherwise, the operation is undefined behavior.
          Ответить
          • Т. е. в сишке разность указателей — это не разность адресов, а разность индексов воображаемого массива, чтобы работала «арифметика указателей», поэтому и происходит деление (компилятор автоматически делит разность указателей на размер элемента массива)?
            Ответить
            • Да. Это же удобство высокоуровневого языка, чтобы инкремент срабатывал как итератор.

              Массив — это указатель (с оговорками).

              Инкремент указателя, увеличивает его значение на размер типа.

              Типичная ошибка начинающих:
              T *x = someArray();
              *x+=sizeof(T); //неверно
              *x++; //переход к следующему элементу
              По сути можно считать инкремент указателя «перегруженным» аки итератор в крестах.

              Edit: кстати там нужно было наоборот, return y-x; но всё-равно это UB-говнище )))
              Ответить
            • Так же, мало кто знает, но в Сишке разность указателей ещё и возвращает специальный тип ptrdiff_t, знаковую (!) версию size_t.

              https://en.cppreference.com/w/c/types/ptrdiff_t

              Там разложены забавные грабли:
              ptrdiff_t is used for pointer arithmetic and array indexing, if negative values are possible. Programs that use other types, such as int, may fail on, e.g. 64-bit systems when the index exceeds INT_MAX or if it relies on 32-bit modular arithmetic.

              Only pointers to elements of the same array (including the pointer one past the end of the array) may be subtracted from each other.

              If an array is so large (greater than PTRDIFF_MAX elements, but less than SIZE_MAX bytes), that the difference between two pointers may not be representable as ptrdiff_t, the result of subtracting two such pointers is undefined.

              For char arrays shorter than PTRDIFF_MAX, ptrdiff_t acts as the signed counterpart of size_t: it can store the size of the array of any type and is, on most platforms, synonymous with intptr_t).
              Ответить
              • приведи пример когда size_t не равен ptrdiff_t
                Ответить
                • Когда ptrdiff_t отрицательный.

                  ptrdiff_t — знаковый (т.к. разность).
                  size_t — беззнаковый (т.к. размер).

                  Потому PTRDIFF_MAX может быть меньше SIZE_MAX.
                  Из-за этого случаются забавные багры.
                  Ответить
                  • спасибо, понятно
                    действительно, size_t (размер) чего либо не может быть отрицательным
                    Ответить
                    • Наставления TheCalligrapher о данном багре:
                      https://govnokod.ru/5026#comment65072
                      Ответить
                      • калиграфер еще больше тему развил
                        https://govnokod.ru/5026#comment65075

                        Действительно, массив не обязан иметь ничего общего с указателями, особенно если вспомнить сегментную модель
                        Ответить
                • из самого маленького указателя (0) вычти самый большой (ULLONG_MAX) - что получится?
                  Ответить
                  • а разве самый большой указатель это ULLONG_MAX а не SIZE_MAX?

                    Получится ptrdiff_t, да
                    Ответить
                    • UINTPTR_MAX
                      Ответить
                      • да, спасибо, макс размер массива не обязан быть равен размеру всей адресуемой памяти

                        Забавно, что мы как-то с Бормандом про это говорили, и я жаловался, что даже в 64х битной джаве или .net у меня размер массива 32х битный int
                        Ответить
                        • Кстати, а ведь на x86_64 указатели знаковые. Они по знаковым правилам работают. А в центре виртуальной памяти дырка которую нельзя замапать.
                          Ответить
                          • По идее я не должен ничего про это хотеть знать, но всё таки спрошу: а почему?
                            Ответить
                            • Все 64 бита мапать дорого, а юзать это пока никто не будет.

                              Поэтому там мапается какой-то небольшой кусок (на 52 бита что ли?). А остальные биты по знаковым правилам догоняются до 64. И получается положительный кусок для юзера и отрицательный кусок для ядра, а между ними дырка. Чтобы когда интел добавит больше битов в пейджтейблы твои указатели продолжили работать без изменений в коде.
                              Ответить
                              • >Все 64 бита мапать дорого, а юзать это пока никто не будет.

                                я думал, что вверху адресного пространства мапается какая-то питушня от ОС, а снизу данные программы.

                                зы: а, понял, ок
                                Ответить
                                • Ядерная память прижата не к середине виртуального адресного пространства, а к самому верху.

                                  | память юзера | ... дырка на будущее... | память ядра |
                                  Ответить
                                  • А что будет с реальными адресами по ту сторону от mmu?
                                    Там сверху будет оборудование, а снизу ram, и адрес ограничен макс объемом памяти cpu?
                                    Ответить
                                    • А к физическим адресам это не относится, насколько я помню. Там тоже не все биты доступны (48?). Но добивается нулями, т.е. всё внизу.
                                      Ответить
                                    • > снизу ram

                                      Да там ёбаная помойка из-за легаси же. Дырки под хуйню в первом мегабайте. Дырки под иса хуйню чуть выше. Дырки под apic и флешку в конце четвертого гига. Ну дальше вроде всё ровно.

                                      Интересно, сколько пирфоманса в кремнии теряется из-за этой совместимости.
                                      Ответить
                                      • еще была дырка в районе 16-го мегабайта, но отключалась в биосе еще в моем глубоком децтве *

                                        * http://lkml.iu.edu/hypermail/linux/kernel/0008.0/0465.html
                                        (но чел не прав, кмк, туда видяха мапилась)


                                        >, сколько пирфоманса в кремнии теряется из-за этой совместимости.

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

                                          Ну это и есть isa memory hole, не?
                                          Ответить
                                          • Мне казалось, что туда мапилась память старых видях (ну исашных, да)
                                            То есть не всех исов, а именно видях
                                            Ответить
                                            • Вроде никому, кроме видях, это не было нужно. Почти все устройства работали через порты. Накопители (жёсткие, сидюки) работали через DMA, т. е. могли срать по любому адресу основной памяти, окно не требовалось.
                                              Ответить
                                          • Вообще ноги растут из такого момента, что раньше железо не умело в PnP, и потому его адреса тупо прибивались гвоздями к какому-то месту в памяти.

                                            Теперь у OSPM и UEFI чуть больше простора для маневра, а размер 64 отодвинул момент пиздеца в отдаленное будущее: соснуть хуйца нам предстоит, когда RAM займет 2^64, но это будет не скоро (если к тому моменту вообще еще будет PC, а не например архитектура с двумя адресными пространствами)
                                            Ответить
              • > result of subtracting two such pointers is undefined

                А потом люди жалуются почему в жабе всё знаковое и массив на всю память не пильнуть...

                З.Ы. Кому вообще понадобилась отрицательная разность указателей? Вот реально, кто-то вычитает больший из меньшего?

                Сказали бы, что вычитать надо в правильном порядке, иначе 2s complement. Хотя это и сейчас можно через каст в uintptr_t перед вычитанием.
                Ответить
      • Только минус надо убрать. Не совсем честно.
        Ответить
        • В вычитании указателей вся суть. Хз как его убрать.

          Кстати код можно было записать и короче, убрав касты.
          div(a,n)
          {
              char d[a];
              struct {char o[n];} *x=(&d)[1], *y=d;
              return x-y;
          }
          Однако в этом случае появляются ворнинги.
          Ответить
      • Как у тебя массивы переменной длины на стеке скомпилились? О_о
        Ответить
        • Разрешение gcc. На цланге соснёшь: https://ideone.com/f5MlFs
          Ответить
        • >массивы переменной длины на стеке скомпилились

          Это же божественная сишка. А в кресты VLA разве ещё не завезли?

          Другое дело, то что VLA дополнительно завёрнут в структуру.
          Ответить
        • апизнайся
          у тебя вижал си?
          Ответить
          • https://govnokod.ru/user/12475/codes

            Нет, он просто крестовик.
            Ответить
            • Если смотреть /codes, то ты 10 лет назад школу окончил.
              Ответить
              • ты давно шкуз окончил?
                Ответить
              • > Pig (U, R) ☓ насрал в #3323
                > Да и щас можно сраться.

                > Pig (U, R) ☓ насрал в #3323
                > Ладно, я сам обосрался.

                )))
                Ответить
    • https://twitter.com/hashtag/define
      Ответить
    • АДМИН СМОТРИТ ДОМ-2
      Ответить
    • АДМИН СМОТРИТ ДОМ-2
      Ответить
    • Хуйня, на плавучих питухах не сработает. И с отрицательными числами хуйня будет
      Ответить
      • Патамучто это плавающий питух, который априори говно. И чем вы быстрее это поймёте, чем будет лучше.
        Ответить
      • На отрицательных нормально всё: см. ассемблерный выхлоп и тесты на ideone.

        А разгадка проста:
        Читать далее ==>
        Ответить
        • > а разгадка проста

          Это UB и конпелятор вообще забил на этот кейс, просто idiv, который он писал для положительных, проканал?
          Ответить
          • https://godbolt.org/z/YaKK8P

            Интересно что он ставит также imul.

            Думаю со сложениями неправильно и не получится. Ведь в дополнительном коде операции со знаковыми и беззнаковыми по сути одинаковы.

            Потому когда оно неявно кастит unsigned size_t в int то результат эквивалентен.

            Вот в прямом и обратном коде это не сработает, но где же взять такую машину или хотя бы компилятор?

            Edit: Я бегло глянул на других архитектурах там тоже умножение знаковое.
            Ответить
            • > умножение знаковое

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

              А вообще - попробуй беззнаковые инты на входе.
              Ответить
              • Проверил RISC V, MIPS, ARM. А также в шланге и штеуд-компилере.
                Везде умножение знаковое.

                Я уж думал из-за интов, написал ему явно:
                >unsigned mul(size_t x, size_t y) {

                Выхлоп не меняется.

                В MIPS кстати сложение беззнаковое addu $2,$4,$5

                Но, как я указал выше, для сложения побитовый результат, интерпретированный как signed будет верным.
                Ответить
            • Прямой код сейчас используется в плавпитухе (у него есть несколько инструкций для целых чисел: FIADD, FIMUL и т. п.). Но этот плавпитух для адресов не используется. Он использовался разве что для 64-битных чисел на 16- и 32-битных машинах, да и то редко (в «Турбо Паскале» был тип данных «Comp» как раз для этого питуха, сишка же для long long высирала сложный код с целочисленными инструкциями, разбивая операнды на половинки).
              Ответить
            • https://godbolt.org/z/Kcaenj
              int add(int x, int y) {
                  return &((void*)x)[y];
              }
              
              int mul(int x, int y) {
                  return sizeof (char[x][y]);
              }
              
              int change_sign(int x) {
                  return mul(x, -1);
              }
              
              int decrement(int x) {
                  return add(x, -1);
              }

              change_sign:
                      mov     eax, edi
                      neg     eax
                      ret
              decrement:
                      lea     eax, [rdi-1]
                      ret

              Впечатляет.
              Ответить
              • И даже через «деление» отрицание работает верно:

                https://godbolt.org/z/9n6MKc

                div(b,a)
                {
                    typedef struct {char lalk[a];} DIVISOR;
                    char bytes[b];
                    return (DIVISOR*)(&bytes)[1] - (DIVISOR*)bytes;
                }
                
                neg(a)
                {
                    return div(a,-1);
                }
                
                div:
                        movsx   rax, edi
                        movsx   rsi, esi
                        cqo
                        idiv    rsi
                        ret
                neg:
                        movsx   rax, edi
                        neg     rax
                        ret
                Ответить
                • Охуеть, конечно. Как конпелятор эти царизмы доказательно свёл к делению/умножению-то?..
                  Ответить
                  • На остатке я его таки немного обломал:
                    rem(a,n)
                    {
                        struct {char o[n];} *x=a,*y=0;
                        return a-sizeof(char[n][x-y]);
                    }
                    
                    rem:
                            movsx   rax, edi
                            movsx   rcx, esi
                            cqo
                            idiv    rcx
                            imul    eax, esi
                            sub     edi, eax
                            mov     eax, edi
                            ret
                    Ответить
                    • Кстати в арме похоже UB
                      rem:
                              mov     r0, #0
                              bx      lr


                      Я не могу понять почему такой выхлоп.
                      На остальных рахитектурах нормально. Например RISC V
                      rem:
                              div     a5,a0,a1
                              mulw    a1,a5,a1
                              subw    a0,a0,a1
                              ret
                      Ответить
                      • > Я не могу понять почему такой выхлоп.
                        Ты же сам писал:
                        >>> When two pointers are subtracted, both must point to elements of the same array object or just one past the last element of the array object (C Standard, 6.5. 6 [ISO/IEC 9899:2011]); the result is the difference of the subscripts of the two array elements. Otherwise, the operation is undefined behavior.
                        y здесь точно не является элементом того же массива, что и x, поэтому на любом входе это UB.
                        Ответить
                        • Там дело не этом.

                          Даже если сделать без UB (через массив), то ассемблерный выхлоп вообще не меняется.

                          На ARM64 он нормальный, а вот на ARMе какая-то питушня:

                          ARM
                          https://godbolt.org/z/qrfdEY
                          rem:
                                  mov     r0, #0
                                  bx      lr


                          ARM 64
                          https://godbolt.org/z/nnPGxE
                          rem:
                                  sxtw    x2, w0
                                  sxtw    x3, w1
                                  sdiv    x2, x2, x3
                                  msub    w0, w1, w2, w0 ; MSUB Rd, Rn, Rm, Ra. → Rd = Ra - Rn*Rm.
                                  ret
                          Ответить
                        • Оказалось дело не в левых указателях, просто нужно было не лениться и явно задавать типы на сигнатуре:
                          https://godbolt.org/z/cnKfG8
                          
                          int rem1(size_t a,size_t n)
                          {
                              struct {char o[n];} *x=a,*y=0;
                              return a-sizeof(char[n][x-y]);
                          }
                          Нетипизированное B-style распиздяйство до добра не доводит.
                          Ответить
            • Гуглим, где встречался обратный код. Он встречался в CDC 6600, в PDP-1, в LINC и в UNIVAC. Т. е. в технике 1960-х на дискретных транзисторах и с памятью на магнитных сердечниках (отсюда и «core dump»). Боюсь, что под такие процессоры нет готового бекенда ни для «gcc», ни для «clang», поэтому кросскомпилятор собирать не из чего.
              Ответить
        • Кстати, попробуй по приколу после этого кода ассёртнуть, что число неотрицательно. Конпелятор скорее всего закешировал утверждения a > 0 и b > 0. И теперь будет их юзать при оптимизации. Даже если число реально отрицательное. И выпилит любые проверки и ассёрты нахер.
          Ответить

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