Boris Litvinov
Boris Litvinov личный блог
29 июня 2018, 09:15

На Арифметике Указателей начал прикуривать. С++

Туговато всасываю!
На Арифметике Указателей начал прикуривать. С++

На Арифметике Указателей начал прикуривать. С++




36 Комментариев
  • Тарас Громницкий
    29 июня 2018, 10:49
    Сочувствую, но не разделяю.
  • Jame Bonds
    29 июня 2018, 10:58
    Главное понять, что указатель на объект это не сам объект. И запомнить операции &, *, new, delete, а также привыкнуть к конструкциям типа int**&x
    Кстати new уже морально устарела и ее обещают запретить в одной из следующих ревизий языка, но во всех книжках она пока что есть.
    А адресная арифметика нужна только для жесткой оптимизации. Можно что угодно написать без нее.
      • Jame Bonds
        29 июня 2018, 11:29
        Борис Литвинов, чтобы писать на mql5 указатели могут и не понадобится. К тому же в mql указатели по синтаксису и идеологии ближе к объектам C#, а не к указателям С++.
        Прочтите по диагонали справку по mql, она хорошо структурирована и похожа на учебник и в бой.
        А чтобы написать dll лучше иметь знания поглубже, но это зависит от того, что dll делает.
          • Андрей К
            29 июня 2018, 12:12
            Борис Литвинов, 
            Вывод прямо в квик графика через lable.
            это подразумевает кучу лейблов на графике? не рекомендую.
            у меня так сделано от 100 до 1000 лейблов. Зависает квик жуть как.
              • Андрей К
                29 июня 2018, 12:29
                Борис Литвинов, скинуть не смогу =)
                я лейблами рисую прямоугольники на графике. За одну торговую сессию примерно 100 лейблов наносится на график. 3-4 торговых сессии и висяк конкретный. Приходится все очищать.
                  • Андрей К
                    29 июня 2018, 12:34
                    Борис Литвинов, на qlua + dll.
                    Сама фишка в том, что квик не может переварить столько лейблов на графике. 
                    Я подумал что вы полный кластерный график будете наносить лейблами. Это подразумевает большое кол-во лейблов.
                      • Андрей К
                        29 июня 2018, 12:39
                        Борис Литвинов, 
                         вы всё правильно подумали, вот так будет
                        я в свое время делал для себя такой движок не на квике. Года 4 назад. Изначально выбрал тупиковый путь, убил пол года только зря. И закрыл проект =) Графика это вообще не мое
                        • tranquility
                          29 июня 2018, 14:46
                          Андрей К, так что-то полезное из этого дела вынести смогли? Я вот тоже делаю свой гиперпродвинутый кластерный анализ, в том числе и стакана, но пока результатом похвастаться не могу, хотя есть определенный оптимизм. Но если бы я это все еще в графику переводил, это бы меня точно добило)) Так, в текстовом формате все сохранить разве что и на питоне представить в виде графиков. Но чтобы риалтайм все шуршало — это же сколько еще дополнительной работы! Тут еще самое стремное — это то, что не знаешь, не придется ли этот огромный пласт работы потом, по большому счету, выкинуть в мусорку…
                          • Андрей К
                            29 июня 2018, 14:51
                            tranquility, 
                            так что-то полезное из этого дела вынести смогли? 
                            ну я несколько лет торговал такой анализ. И хорошо очень заработал и хорошо спустил =)) Сейчас этот вид анализа уже давно в прошлом. Я это все дело достаточно сильно усовершенствовал и мне это все стало незачем.

                            Весь этот объемный анализ очень критичен к некоторым стадиям рынка. Меняется рынок (ну например ушли объемы с определенного фьюча), если трейдер не успевает перестроиться, это может стать критичным для него. Такая торговля, по крайней мере для меня, не очень.
      • Unworldly
        01 июля 2018, 03:47
        Jame Bonds, мне вот  интересно, то что уже прошел для того что бы начать писать на MQL5 хватит, или идти дальше не оглядываясь.

        Борис Литвинов, вот-вот, оптимизм так и плещет через край! 
        Главный прикол здесь в том, что в MQL нет адресной арифметики

        А так же DDL первую в жизни свою хочу, вкрутить в квик!
        У меня там сейчас на qpile и lua всё

        Учебник неправильный. То, что ничего не падает, это просто везёт. Вот, можно посмотреть, какой мусор печатается вместо 10, 11, 12, 10 (наверху есть зелёная кнопочка Run, а также можно нажать F9).

        Если бы было понимание адресной арифметики, то это было бы замечено сразу в исходном коде. Такие ошибки как раз и называют «переполнением буфера».

        Но это — явные ошибки. А есть ещё неявные. Вот эта программа — неправильная. Факт того, что она печатает то, что и ожидается, означает, что нам просто везёт, потому что программа компилируется там ещё и без оптимизации. Стоит включить оптимизацию, и может произойти всё, что угодно, вплоть до падения.

        Кстати, f1() специально написана на голом C, а f2(), которая делает в точности то же самое, — с использованием всего лишь нескольких специфических для C++ механизмов. Они не являются необходимыми, просто напихал слегка, чтобы дать хотя бы лёгкое представление о сложности «недетского» C++. В этой функции использовано явно меньше 1% всех имеющихся механизмов C++.

        Именно поэтому я и предлагаю ограничиться голым C. Среди программистов-то, знающих голый C, трудно найти человека, который, хотя бы, более или менее знает C++.

        Язык C — относительно (относительно!) небольшой, универсальный и достаточно низкоуровневый, чтобы исподволь сформировать также дополнительное понимание, как всё работает в компе. Позволит, в числе прочего, как писать свои DLL'ки, так и использовать его потом как неплохую базу для изучения других языков, если понадобится. Ну, или почти с нулевым усилием использовать, хоть и плюясь, MQL.

        В приведённом примере есть выражение 256u**p+++*p++. Я специально записал его без пробелов, чтобы подчеркнуть «непростоту», обычно пробелы ставят, где надо.

        Во-первых, как мне кажется, не каждый разберётся, что это тут такое понаписано. И буква u ещё какая-то затесалась… И, главное, это всё компилируется и работает...

        Расставлю скобки: 256u * (*(p++)) + (*(p++)).

        Суффикс u означает, что константа беззнаковая. Итак, 256 умножается на значение из первой «ячейки», указатель продвигается на следующую и к этому прибавляется значение из следующей ячейки, раз указатель до этого продвинулся, и указатель продвигается ещё раз.
        Грубо говоря, в математическом виде это могло бы выглядеть так: sum = 256 * a[1] + a[2].

        Теперь начинаются тонкости, почему программа неправильная.

        Во-первых, подвыражения могут вычисляться в любом порядке, то есть, сначала может быть вычислена правая часть (*(p++)), а только потом левая 256u * (*(p++)). Это сделано для того, чтобы компилятор имел больше возможностей для  оптимизации кода.
        В данном случае математический эквивалент станет таким: sum = 256 * a[2] + a[1].

        Потому что правое подвыражение же тоже продвигает указатель. Совершенно другой результат получается.

        Более того, продвигание указателей может быть отложено на самый последний момент, и тогда математический эквивалент станет ещё интереснее: sum = 256 * a[1] + a[1].

        Но это всё — ещё не самое страшное. Из-за этого всего просто результат был бы неправильным, и — всё. Звучит, — да, интересно. Просто неправильно вычисляется, а так — ничего страшного. 

        Но есть ещё кое-что. В этом самом выражении дважды происходит изменение одной и той же переменной (указателя p). А эта ситуация есть, так называемое, undefined behavior.
        Страшная штука. Она означает следующее: при исполнении данного кода может быть всё, что угодно, от нормального выполнения, до условно, форматирования жёсткого диска. На практике, всё же, всё не так страшно: не до форматирования жёсткого диска, а, «всего лишь» до падения программы.

        Причём, современные компиляторы при оптимизации используют этот undefined behavior в свою пользу: скорее всего, код, соответствующий этому выражению, будет просто выкинут. Причём, молча. Компилятор будет доволен: мощно соптимизировал. 

        И это всё ещё голый С, без всякого, там, C++.

        А теперь вопрос: даёт ли тот «учебник» с неправильной программой, в которой «нахально торчит» неоднократное переполнение буфера, а также стиль обучения «по верхушкам» понимание вот этого всего?

        Насколько легко «накодить» такого, вроде бы, «безупречного» кода, из-за которого программа будет падать?

        Каковы шансы, обучившись «по верхушкам», хотя бы понять, из-за чего всё падает или неправильно вычисляется после включения оптимизации?

        Надеюсь, я дал представление, насколько всё непросто, и сколько там всего. В том числе, не очевидного, тонкого и, на восприятие не программиста, мягко говоря, странного.
    • Eskalibur
      29 июня 2018, 20:16
      Jame Bonds,  что использовать вместо new?
      • Андрей К
        29 июня 2018, 20:19
        Eskalibur, mmap наверное =))
      • Jame Bonds
        29 июня 2018, 20:57
        Eskalibur, умные указатели
        https://habr.com/post/352570/
        • Андрей К
          30 июня 2018, 11:29
          Jame Bonds, видно придется еще лет 10 на 11 стандарте работать =))
          • Jame Bonds
            30 июня 2018, 14:16
            Андрей К, нечего деградировать, вперед — читать мануалы!
            )
            • Андрей К
              30 июня 2018, 14:35
              Jame Bonds, все эти рюшечки очень накладны по времени.
              А если их еще внедрять в готовый проект, то весь тайминг алгоритмов может уехать очень сильно. Я поэтому по старинке =)
    • Unworldly
      01 июля 2018, 02:15
      Главное понять, что указатель на объект это не сам объект.

      Jame Bonds, этого понимания совершенно недостаточно для понимания адресной арифметики.

      И запомнить операции &, *, new, delete, а также привыкнуть к конструкциям типа int**&x

      А что делать, если после запоминания этих операций встретятся операции new[] и delete[]?

      И — только я привыкну к конструкциям типа int **&x, как обязательно встретится что-нибудь этакое: int const volatile * const *volatile (&&x)[5], и — что мне тогда делать?
      Тупая зубрёжка здесь не спасёт. Требуется понимание.

      Кстати new уже морально устарела и ее обещают запретить в одной из следующих ревизий языка, но во всех книжках она пока что есть.

      Эта статья на Хабре — за первое апреля, там даже среди прочих меток у статьи есть специальная метка «1 апреля». Операция new настолько фундаментальна для C++, что после её изъятия это будет уже совсем другой язык.

      Тем не менее, можно писать очень сложные программы без единого использования операции new, но при этом вовсю используя динамическую память. Более того, именно так и полезно это делать.

      C++ определяется не книжками, а стандартами. То, что в книжках, — это вторично. Компиляторы реализуют согласно стандарта, а не книжки.

      А адресная арифметика нужна только для жесткой оптимизации. 

      Вот откуда это взялось?

      Можно что угодно написать без нее.

      Работа с массивами — уже адресная арифметика. Почему в C/C++ возможны конструкции типа 1[«No»]? Именно поэтому. Вот пример (наверху есть зелёная кнопочка Run, а также можно нажать F9). Если захочется поиграться, рядом с Run есть кнопочка Fork this. Можно будет редактировать исходник.

      Опять же, если есть понимание, то выяснить, что это за чертовщина такая, &0[«Hell»], не составит никакого труда...
      Вроде бы, простейшая программа, а, чтобы понять, почему она печатает «Hello!», довольно много тонкостей всяких знать надо.

      Когда критикуют излишний оптимизм начинающих трейдеров, приводят пример, что инженерам, например, а, особенно, врачам довольно долго учиться надо, а потом ещё и практиковаться, прежде чем они что-то реальное смогут в своей области. И дальше задают вопрос, почему вы думаете, что в трейдинге не так?

      Вот и с программированием такая же «петрушка». Причём, программирование уже стало в этом смысле «тяжелее» медицины.
  • Да как то слабо, даешь ассемблер!!!
      • Unworldly
        01 июля 2018, 04:41
        Борис Литвинов, там вообще сплошные парадоксы и прочие квантовые эффекты наблюдаются.

        Например, есть некий кусок кода с временем исполнения C, условно состоящий из двух «подкусков» кода с временами исполнения A и B. После «оптимизации» второго «подкуска» его время выполнения B уменьшается. Однако, при этом, при неизменном коде первого «подкуска» его время исполнения A увеличивается, причём сильнее, чем уменьшается время исполнения второго «подкуска» B.

        В результате, в выражении C = A + B, при уменьшении B сумма C увеличивается. Потому что уменьшение B само по себе вызывает ещё большее увеличение A.

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

        Так что ассемблер — это то, куда совершенно точно соваться не следует. Разве, что для развлечения. 
  • Андрей К
    29 июня 2018, 12:36
    А то что заинтересовались памятью, это путь к качественному росту =). Пригодится вам или нет в итоге, но это один из немногих путей для создания эффективных приложений. Но это уже когда счет идет на миллисекунды и гораздо ниже.
      • Андрей К
        29 июня 2018, 12:44
        Борис Литвинов, почему нет? хорошие хотелки =)
        надо наверное много куда потыкаться, чтобы найти свою тропинку
  • int b = *pa++
    Сначала возвращается значение по указателю, потом инкремент адреса указателя.
    (*pa)++ это инкремент значения по указателю.
    • tranquility
      29 июня 2018, 14:50
      Багатенький Буратина, да, лишняя пара скобок еще никого беднее-болнее-мертвее не сделала, так что и
      int b = *(pa++);
      тоже имеет право на существование!
      • tranquility, тут access violation если не повезёт
        • tranquility
          29 июня 2018, 15:40
          Багатенький Буратина, а, я просто привык уже с итераторами дело иметь, там хоть есть возможность проверить, вышел итератор за границы контейнера, или еще нет. А так, конечно, любой программист, который пишет большой проект будет плеваться при виде кода с указательной арифметикой и метками. Единственное, чем его можно оправдать — это если он используется локально, изолированно, в очень критичном месте.
      • Андрей К
        29 июня 2018, 15:39
        tranquility, на компиляторах Си строго действует правило — чтение слева. По крайне мере на всех по иксами.
  • tranquility
    29 июня 2018, 15:48
    Да, небольшое замечание от меня уже. Использование Console::WriteLine для отображения кириллицы — это хорошо, но оно лишает тебя одного большого преимущества: кроссплатформенности! Поэтому я бы посоветовал все таки найти способ использовать std::cout (std::wcout). Для этого, если я не ошибаюсь, достаточно в начале функции main() сделать вызов:
    setlocale( LC_ALL, «russian_Russia.1251» );
    вот не знаю, отработает ли это нормально под линукс, но под виндой — должно. По крайней мере, «сбербанк» в файл у меня оператором вставки ("<<") у меня пишется корректно.
    Да, вот этот пример замечательно работает:

    #include <iostream>

    using namespace std;

    int main()
    {
    setlocale( LC_ALL, «russian_Russia.1251» );

    cout << «С новым годом, товарищи!» << endl;

    getchar();
    return 0;
    }

  • Туговато всасываю!

    Терпи!
    Заглатывай!

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн