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

    −1

    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
    #include <stdio.h>
    #include <stdlib.h>
    
    int * ptr;
    
    int * getptr()
    {
      puts("getptr");
      return ptr;
    }
    
    int jump()
    {
      puts("jump");
      ptr = (int*)malloc(sizeof(int));
      return 1337;
    }
    
    int main()
    {
      ptr = (int*)malloc(sizeof(int));
      *ptr = 0;
    
      *( getptr() ) = 1;
      printf( "*ptr = %i\n\n", *ptr );
    
      *( getptr() ) = (jump(), 100);
      printf( "*ptr = %i\n\n", *ptr );
    
      *( getptr() ) = jump();
      printf( "*ptr = %i\n\n", *ptr );
    
      return 0;
    }

    ШИКАРНО:

    Start

    getptr
    *ptr = 1

    jump
    getptr
    *ptr = 100

    getptr
    jump
    *ptr = 0

    0

    Finish

    Запостил: bugspawn, 15 Ноября 2017

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

    • подсказка: сей фрагмент иллюстрирует волшебный порядок вычисления операндов в С/С++
      Ответить
      • Подсказка: нет никакого волшебного порядка, порядок вычисления зависит от компилятора, твой код содержит UB.
        Пример с 100 работает из-за наличия "точки следования" в виде оператора ,
        Ответить
        • тест №3:
          - частично вычисляется левая часть = (выполняется getptr())
          - вычисляется правая часть (выполняется jump())
          - продолжает вычисляться левая часть (выполняется *)
          не, ну это КАК ВООБЩЕ?
          т.е. есть оператор =, у него 2 операнда, и компилятор такой взял чучуть посчитал слева, потом чучуть справа, потом подумал решил снова влево посчитать

          тест №2:
          - вычисляется правая часть (выполняется jump() и ",")
          - вычисляется левая часть = (выполняется getptr() и потом *)
          каким таким боком "," внутри правого операнда влияет на то, когда в левом операнде будет посчитано значение в скобках (getptr()) - до или после вычисления правого операнда???
          Ответить
          • Ну посмотри асмовый листинг, и узнай почему копелятор так делает.
            Это же UB, он имеет право что угодно сделать. При включенной оптмизации компелятор вообще может выкинуть UB код: дескать можно вообще ничего не делать, раз UB.
            Ответить
            • > Это же UB, он имеет право что угодно сделать.

              правильнее: что бы разрешить компилерам делать что угодно (== агресивная оптимизация вызовов функций), стандарт оставляет это как UB.
              Ответить
              • Лолшто. Уб - это некорректный код, который в общем случае нельзя диагностировать при конпиляции, не более. Как висячий указатель разыменовать. Стандарт не вводит уб ради каких-то там оптимизаций. Просто конпиляторы оптимизируют в предположении, что программист не пишет некорректный код, не более.
                Ответить
                • > Уб - это некорректный код,

                  я не говорил про общий случай. я говорил про конкретный случай, проиллюстрированый в говнокоде.

                  стандарт может сделать корректным код сверху, но это преднамеренно не делается.
                  Ответить
                  • Да, почитал топик, ты все правильно сказал. Извини за выпад. Иногда хочется поумничать неразобравшись. Прости.
                    Ответить
                    • internal internet error. apology identified. undefined behavior detected. aborting.

                      WAS THIS PAGE HELPFUL? YES/NO. TELL US MORE. YOUR OPINION IS IMPORTANT TO US!
                      Ответить
                • в общем случае нет, а в частном случае иногда можно. Например, разыменовывание нулевого указателя - точно UB, к гадалке не ходи.
                  Ответить
                  • Нет. Разыменование нулевого указателя в sizeof() не является UB
                    char *ptr = NULL;
                    size_t sz = sizeof(*ptr); // не UB
                    Ответить
                    • А тут фактически нет разыменования, потому что sizeof — это хак. Это синтаксический сахар. Чтобы определить размер данных, не нужно иметь к ним доступ. Размер определяется косвенным путём (в данном случае компилятор просто залезает в определение переменной ptr).

                      В процессорах x86 есть похожий хак: инструкция LEA. Она тоже не читает данные, а просто вычисляет их адрес.
                      Ответить
                      • https://ru.wikipedia.org/wiki/Синтаксический_сахар
                        > Принципиально то, что синтаксический сахар, теоретически, всегда можно удалить из языка без потери его возможностей — всё, что можно написать с применением синтаксического сахара, может быть написано на этом же языке и без него. Таким образом, синтаксический сахар предназначен лишь для того, чтобы сделать более удобным для программиста написание программы.

                        Допустим:
                        struct shit {
                           uint8_t a;
                           uint32_t b;
                           uint 8_t c;
                           uint32_t d;
                           uint 8_t e;
                        };
                        ...
                        size_t sz =  sizeof(struct shit);

                        И как это переносимо написать без sizeof? Создавать массив из двух элементов struct shit и вычитать разницу адресов между нулевым и первым элементом для такого массива? Такой подход будет неточным.
                        Для одной структуры с учетом выравниваний полей допустим будет такая хуита:
                        |      uint8_t a    |    uint32_t b     |      uint8_t c    |    uint32_t d     | uint8_t e|
                        |8bit|____|____|____|8bit|8bit|8bit|8bit|8bit|____|____|____|8bit|8bit|8bit|8bit|8bit|

                        Но если сделать массив из двух таких структур, этот uint8_t e уже сожрет себе все 4 байта, и разница между одним и другим указателем в массиве из двух таких структур будет не равна размеру одной структуры в байтах
                        |      uint8_t a    |    uint32_t b     |      uint8_t c    |    uint32_t d     |      uint8_t a    | ...
                        |8bit|____|____|____|8bit|8bit|8bit|8bit|8bit|____|____|____|8bit|8bit|8bit|8bit|8bit|____|____|____| ...
                        Ответить
                        • А еще ж есть всякие битфилды... в общем я не думаю что существует некий костыльный способ, который может заменить sizeof во всех возможных ситуациях и позволит обойтись полностью без sizeof
                          Ответить
                          • sizeof(имя_структуры). Заменяет во всех ситуациях, но можно ошибиться, в отличие от сахара со звездочкой.
                            Ответить
                            • не заменяет, если этот sizeof() используется в макросе
                              #define COPYSHIT(dest,src) memcpy(dest, src, sizeof(*src))
                              Ответить
                              • Никто не мешает передать тип параметром.
                                Ответить
                        • > разница между одним и другим указателем в массиве из двух таких структур будет не равна размеру одной структуры в байтах

                          1) равна, т.к. размер структуры включает паддинги
                          2) и соответственно sizeof возвращает ровно количество байт между двумя структурами в массиве
                          Ответить
                          • Почему тогда не сделали синтаксического сахара чтоб узнавать размер структуры без учета паддинга в самом конце?
                            Ответить
                            • И зачем нужно узнавать размер без падинга?
                              Ответить
                              • Для записи в файл. Или например чтоб меньше байт копировать, если скопировать надо всего одну такую структуру
                                Ответить
                                • > Для записи в файл чтоб меньше байт копировать
                                  Ты же знаешь, что ОС всё равно блоками пишет, да?
                                  Ответить
                                  • Да, но в файл хочется записать структуру без ненужного мусора в конце.
                                    Ответить
                                    • Вообще, намного полезнее была б фича чтоб можно было последовательно пройтись по всем полям структуры, ну типа
                                      struct shit {
                                         uint8_t a;
                                         uint32_t b;
                                         uint8_t c;
                                         uint32_t d;
                                         uint8_t e;
                                      };
                                      
                                      struct shit somecrap;
                                      ...
                                      fwrite(&(somecrap.a), sizeof(somecrap.a), 1, fd);
                                      fwrite(&(somecrap.b), sizeof(somecrap.b), 1, fd);
                                      fwrite(&(somecrap.c), sizeof(somecrap.c), 1, fd); // чтоб всю эту питушню не писать
                                      
                                      // а каким-нибудь foreach-ем пройтись

                                      Вобщем-то сишечка довольно убогий язык, но в плюсах-то эту проблему решили же, да?
                                      Ответить
                                      • > Вообще, намного полезнее была б фича чтоб можно было последовательно пройтись по всем полям структуры

                                        Я так и делал всегда. Там же строки могут быть, указатели, вот это всё. Плюс byte order иногда важен. Просто пишешь простенький сериализатор/десериализатор и не выёживаешься. Ну или просто используешь какой-нибудь asn1/protobuf.
                                        Ответить
                                        • Вот и получается, что подобные проблемы приходится решать всякими там кодогенераторами (какой-нибудь asn1/protobuf) или писать все эти сериализации/десериализации руками.

                                          > Там же строки могут быть, указатели, вот это всё.

                                          Можно придумать некий внутриязыковой механизм, который бы позволял программировать все такие вариации, скажем
                                          struct data_8 {
                                            uint8_t sz;
                                            uint8_t *data;
                                          };
                                          
                                          struct data_16 {
                                            uint16_t sz;
                                            uint8_t *data;
                                          };
                                          
                                          struct shit {
                                            uint32_t someshit;
                                            struct data_8 a;
                                            struct data_16 a;
                                          };
                                          
                                          struct shit somecrap;
                                          
                                          COMPILETIME_FOREACH_STRUCT (TYPEOF(somecrap); somecrap)
                                          {
                                            STOREDATA(somecrap##TYPE, fd);
                                            // для "struct data_8" и "struct data_16" будут особым образом объявлены отдельные функции
                                            // которые бы отвечали за то, как это обрабатывать
                                          }

                                          В общем тут как раз таки гомоиконность нужна. В плюсах есть лишь какие-то ограниченные костыли, вроде констэкспров, шаблонов. Есть ли какие-нибудь способы распарсить в компилтайме тип структуры (из чего она состоит) и на основе этого порождать некий код, который что-то там должен делать?
                                          Ответить
                                          • > есть ли в плюсах
                                            Пока только ограниченное говно типа boost::fusion, где поля надо руками перечислять (типы само надёргает).

                                            Но обещают завезти нормальную рефлексию и кодогенерецию. Лет через 5-10.
                                            Ответить
                                          • > приходится решать всякими там кодогенераторами

                                            У кодогенераторов есть преимущество: они могут генерить код на разных языках из одного описания.
                                            Ответить
                                            • > код на разных языках
                                              Правда при этом приходится добавлять ещё один язык для таких описаний...
                                              Ответить
                                              • > Правда при этом приходится добавлять ещё один язык для таких описаний...

                                                ... что не обязательно является чем-то плохим.
                                                Ответить
                                                • попытки решить проблемы одних языков созданием других лишь приводят к увеличению числа проблемных языков
                                                  Ответить
                                                  • > попытки решить проблемы одних языков созданием других

                                                    Так-то в каком-нибудь CL можно один раз написать макрос и генерить из
                                                    (def-serializable-struct cat
                                                      (name string)
                                                      (volume (cm³ fixnum))
                                                      (weight (g fixnum)))
                                                    сериализаторы/десериализаторы/парсеры/валидаторы/модули для nodejs/даже небо/даже аллаха. Но люди продолжают придумывать новые, более другие языки.
                                                    Ответить
                    • Правда не UB? И чему же оно будет равно?
                      Ответить
                      • sizeof(char) всегда 1
                        Ответить
                        • FOO *bar;
                          иными словами sizeof(*bar) всегда есть sizeof(bar) даже если bar это данглинг поинтер?

                          Ну звучит логично: не надо разыменовывать
                          Ответить
                    • але гараж. sizeof работает над типом а не над данными. Вот в реализации offsetof, наприме, формальный UB.
                      Ответить
          • Ты нуб штоли? Конпилятор переупорядочивает инструкции так, чтобы они выполнялись быстрее. Иди читать про точки следования в плюсах и конвеер в процессоре, нуб.
            Ответить
        • единственное объяснение что мне приходит в голову, это что сперва вычисляется то что в скобках с наибольшим уровнем вложенности по обе стороны от =, причем справа налево, потом от большего уровня вложенности к меньшему
          Ответить
          • Думаю, тем, кто пишет код с UB и винит компилятор в нелогичном поведении, лучше идти напитон. Там наверняка порядок вычисления присваиваний детерминирован. Специально для творческих личностей, желающих этим порядком воспользоваться.
            Ответить
            • Не только питон. Обычно в ЯПах под JVM и .NET тоже нет UB (ну, кроме race conditions). А в CPython и MRI Ruby нет и их:)

              В swift, я думаю, что тоже UB нет (во всяк случае ябловый UBSanitizer в шланге работает только для C)
              Ответить
              • В C# есть https://stackoverflow.com/a/1860953
                Но UB там возникает не на ровном месте, в отличии от плюсов и сишки
                Ответить
                • >>unsafe" block
                  ну так-то оно и в Java есть sun.misc.Unsafe

                  Даже в питоне можно взять cTypes и стригеррить черте-что

                  Мы же про "нормальное" использование языка.
                  Ответить
                  • Не, можно и без unsafe, например вот https://habrahabr.ru/company/enterra/blog/243371/ :

                    > Если кратко, то содержание поста сводится к рассмотрению следующего примера:
                    [Description(Value)]
                    class Test
                    {
                        const string Value = "X\ud800Y";
                    }


                    > Строка в C# представляет собой последовательность слов в UTF-16. А значение "X\ud800Y" не особо хорошее, т.к. включает в себя старшее слово суррогатной пары 0xD800, после которого должно бы идти младшее слово (интервал 0xDC00..0xDFFF), но вместо него идёт Y (0x0059). Проблемы начинаются из-за того, что в IL-коде для хранения аргументов конструктора атрибута используется UTF-8. Впрочем, у Джона Скита всё очень хорошо расписано, всем советую прочитать оригинальный пост.

                    > Меня заинтересовало, как же будут себя вести MS.NET и Mono в этой непростой ситуации (подробная заметка). А вести они себя будут по-разному. Первое различие можно увидеть во время компиляции. MS.NET положит значение строки в метаданные в виде 58 ED A0 80 59, а Mono — в виде 58 59 BF BD 00 (оба значения являются невалидными UTF-8 строчками). Второе различие можно пронаблюдать запустив полученные приложения. MS.NET сможет запустить обе версии и успешно достанет значение аргумента атрибута (в виде 0058 fffd fffd 0059 и 0058 0059 fffd fffd 0000 соответственно), а Mono поперхнётся настолько невалидной строкой и вернёт null в каждом из случаев. Из-за этого маленький пример Джона Скита сразу упал, когда я попытался запустить его под Mono.
                    Ответить
                    • в общем если хотите чтоб без UB - пишите на брайнфаке. Во всяких нетривиальных языках всегда можно какую-то ерунду не учесть, типа кодировок или вообще фиг пойми чего, из-за чего возникнет UB, не потому что специально хотели делать UB, просто никто о таком нестандартном сценарии не задумывался
                      Ответить
                    • Охренеть!! ТАкой литерал не должен был скомпилироваться!
                      Это дыра в стандарте
                      [quote]
                      According to the ECMA-334 document (p. 473):

                      A program that does not contain any occurrences of the unsafe modifier cannot exhibit any undefined behavior.
                      [/quote]

                      Но вообще мой cl четко видит тут три буквы: первая и последняя обычные, а средняя занимает 3 байта в UTF-8 (мне кажется это какой-то такой плейн где всякие asian languages, а нет, это невалидная хуйня (которую уникод обозначает значком [?]) )
                      class Program
                          {
                              const string Value = "X\ud800Y";
                      
                              static void Main(string[] args)
                              {            
                                  Console.WriteLine(String.Join(" ", Encoding.UTF8.GetBytes(Value).Select(b => b.ToString())));
                              }
                          }
                      // 88 239 191 189 89
                      // Тащемто попытка напечтать то и выводит: X[?]Y.
                      Ответить
              • # UB (ну, кроме race conditions)

                Это не UB
                Ответить
          • После игры "угадай что делает компелятор по эфектам от UB" вам предстоит уровень "возьми платформу с weak memory model типа ARM или PowerPC и угадай в каком порядке CPU выполнит твой код"
            Ответить
            • это поэтому на ведроидах все без исключения постоянно крэшится? :)
              Ответить
              • Откуда статистика?

                Ведроид бывает
                1) не только на arm (mips, atom)
                2) 90% софта там писано на java/kotlin под ART. А там действуют правила JMM и JLS.

                зы: ну ты понимаешь же что с weak memory model можно прекрасно жить, как и с любой другой memory model, главное не завязываться на странные спец-эффекты.
                Любой CPU всегда можно попросить не реордерить ничего (обычно с помощью интринсика или ключевого слова ЯПа, которое затем превращается в инструкцию типа fence или как-то так)
                Ответить
                • статистика по личным и очевидцев наблюдениям)
                  понятно что жить можно и любой подводный камень можно переступить
                  но должно же быть какоето объяснение феномену...
                  на вики пишут что косяки JMM с 2004 года недействительны
                  Ответить
                  • Я думаю что падения связаны с более низким уровнем review (у ябла он сильнее), а не с memory model ибо см. пункт 2.

                    У JMM нет UB кроме race conditions, но это не совсем UB так как не зависит от компилятора (а зависит от количества ядер, шедулера операционной системы, количества и тяжести других процессов, и месяца китайского календаря).

                    Объяснение очень простое: чем слабее гарантии -- тем больше свободы у инженеров в разработке проца, а значит тем лучше он у них может получиться.

                    Ты ведь наверное тоже не любишь делать свои интерфейсы и API публичными, что бы на них сразу же завязались 150 человек, и ты бы потом не мог их отрефакторить
                    Ответить
    • Автор, срочно переходи на PHP!
      Там всё, как тебе нравится: https://ideone.com/IY0KUp
      Ответить
      • Что такое $$ptr в данной программе?
        Ответить
        • Обращение к переменной с названием в переменной $ptr.
          Ответить
          • А переменные с названиями 0, 1 и 2 в "PHP" бывают?
            Ответить
            • В ПХП возможно всё, если делать это через жопу.
              Напрямую к ним обратиться нельзя - это синтаксически неверно. Но если использовать $$, то именем переменной может быть любая строка и, следовательно, что угодно, что в нее конвертится.
              https://ideone.com/svS2sO
              Ответить
              • Я догадывался... Но как объяснить непрограммистам, над чем я ржу?

                P.S. Это можно публиковать отдельным говнокодом.
                Ответить
      • Но в пыхе нет указателей, как же без них отстрелить себе ногу?
        Ответить
        • Когда я был пыховцем, у меня не было указателей чтобы отстрелить себе ногу, но мне хотелось ходить без ноги, как взрослые С++ программисты, и тогда я использовать SQL инъекции, глобальные переменные и eval()
          Ответить
    • Переходи на С++17
      Ответить

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