Блог пользователя BorN

Автор BorN, 11 лет назад, По-русски

Помогите справиться с заданием. Идей нету вообще. Заранее спасибо

Условие:

Я.п. с++. Есть две подфункции одного уровня. Func1 и func2. Обе функции типа void, не получают и не возвращают значений. В обеих функциях есть локальная переменная temp. Функция func2 вызывает func1, а затем выводит строку со значением своей переменной temp на экран. Вот код

Задача:

функция func2 должна выводить значение, полученное от пользователя функцией func1 (то есть она должна каким-то образом получить это значение, для этого в ней и зарезервирована своя переменная temp, для удобства). Для реализации этого допускаются любые программно-аппаратные средства, КРОМЕ:

  1. Изменения типов функций. Они должны оставаться void и не должны ни к чему кастоваться при вызове. Строго говоря, ни в одной из функций не должна находиться инструкция return.

  2. Переноса инструкции cin >> temp из первой функции во вторую. Или добавление второй такой инструкции в func2. Значение должно запрашиваться один раз и только из функции func1. Выводиться это значение обязательно, соответственно, должно непременно из функции func2.

  3. Использования глобальных переменных. А также всяческих статических классов и прочей ерунды. Добавляться в ходе решения что-то должно только в коде этих двух функций.

  4. Использования указателей, ссылок.

  5. Записи\чтения файлов.

  6. Использования системных средств — семафоров, счетчиков, флажков, блокировок и т.д. А также любых других средств API.

  7. Использования сетевых соединений

  • Проголосовать: нравится
  • +6
  • Проголосовать: не нравится

»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

А откуда задача?

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    сам не знаю. мне один знакомый подсунул

»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Похоже, что в задаче требуется грамотно поломать стек)

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Без указателей не сможешь :).

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится +17 Проголосовать: не нравится

      Easy:

      http://pastebin.com/Zi1YtW47

      Это только в MS C++ работает. Можно в запуске проверить.

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Легко можно: в func1 делаем что-то типа

      float arr[1];
      
      arr[-42] = temp;

      Конечно, при условии, что с выравниванием всё ок.

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Использовали ли Вы указатели? Строго говоря, да (arr является указателем на начало зарезервированного в стеке блока памяти). А что об этом думает тот идиот препод, который дал задачу?

        Использовал ли dalex глобальные переменные? Строго говоря, регистр процессора можно считать глобальной переменной. А что думает препод?

        Вот из-за этой субъективности задачи подобного рода меня страшно бесят.

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится +13 Проголосовать: не нравится

          Оу, func1 и func2 это тоже указатели. Занудство)

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится +2 Проголосовать: не нравится

            Если уж быть занудой до конца, в строчке cin >> temp; происходит передача аргумента по ссылке в операторную функцию.

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

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится +4 Проголосовать: не нравится

          Строго говоря, да (arr является указателем на начало зарезервированного в стеке блока памяти)

          Александр, вы, кажется, в основном пишете на яве, так что вам простительно. Указатель и массив с точки зрения C++ различные типы данных, так что объявление float arr[1]; не использует указатели.

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится +13 Проголосовать: не нравится

            Мне пришлось всё-таки признать, что он прав. Я посмотрел, оператор квадратных скобок ожидает указатель как один из операндов, поэтому в

            arr[-42]

            arr сначала будет преобразован в указатель, и только потом выполнится оператор [].

»
11 лет назад, # |
  Проголосовать: нравится +40 Проголосовать: не нравится
  • »
    »
    11 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +16 Проголосовать: не нравится

    Это намного лучше, чем у меня.

    Правда, вызов func1() находится внутри try, что вроде не соответствует заданию.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Круто:))

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится

    Есть такой трюк. Ещё один юз:

    void fnd (Tree* p, const string& s) 
    {
       if (s == p->str) throw p;
       if (p->left) fnd(p->left,s);
       if (p->right) fnd(p->right,s);
    }
    
    Tree* find(Tree* p, const string& s)
    {
       try {
         fnd(p,s);
       }
       catch (Tree* q) {
         return q;
       }
       return 0;
    }
    
    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      А какой смысл этого трюка? Что, try/catch работает быстрее return?

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится +2 Проголосовать: не нравится

        мб все остальные ветки рекурсии автоматически обрубаются?

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится +5 Проголосовать: не нравится

          А при return что, не обрубаются?

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится +5 Проголосовать: не нравится

            не должны. я встал в какую-то вершину. спустился из нее в правого сына и в левого. в правом внезапно нашел строку s и return-ул ее. но из левого сына я буду продолжать спускаться до листьев. другое дело, что было бы логично, если однажды в ходе программы функция fnd() уже облажалась, сразу все ее вызовы во всех местах из стека потереть.

            • »
              »
              »
              »
              »
              »
              »
              11 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
              1. Если что-то нашел в правом поддереве, значит, левое уже было полностью обработано.

              2. Какая разница, по какой инструкции потереть из стека вызовы, по return или throw?

              3. Раскрой уже нормальный акк.

              • »
                »
                »
                »
                »
                »
                »
                »
                11 лет назад, # ^ |
                Rev. 3   Проголосовать: нравится +16 Проголосовать: не нравится
                1. что вы меня путаете. встал я в вершину. пошел влево. там поискал что-то. если у меня везде return, то все равно дело кончится тем, что у меня свернется if (p->left) fnd(p->left, s) . но от этого не перестанет существовать (p->right) . и я все равно пойду в него. другое дело, что если я что-то кинул в какой-то вершине дерева вызовов, то все дерево (maybe) потрется.
                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  11 лет назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится

                  Все, я понял, что имеется в виду, я сам немного затупил, претензий больше не имею

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Смысл трюка в том, чтобы сделать неочевидную и медленно работающую конструкцию.

        Так и хотел написать: "Люди, никогда так не пишите!".

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится +5 Проголосовать: не нравится

          А с чего бы эта конструкция медленно работала, кстати? В варианте без кидания исключения будет лишняя проверка возвращаемого значения fnd(p->left, s)

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
            Rev. 3   Проголосовать: нравится +3 Проголосовать: не нравится

            Казалось бы, это давно известный факт, что обработка исключений дает огромный overhead.

            Однако, бенчмарк (DFS на вырожденных в цепочку деревьях) выявил весьма интересную вещь: при размере цепочки до примерно 6500 версия с if-ом работает быстрее, на больших тестах она начинает проигрывать версии с исключениями. Видимо, причина в быстром сворачивании стека.

            Я сейчас ради интереса напишу версии с setjmp/longjmp и с ручным сворачиванием стека.

            UPD. Написал с setjmp/longjmp, обходит версию с if-ом начиная где-то с 22 вершин.

            UPD2. Исходя из результата бенчмарка с setjmp/longjmp, писать ручное сворачивание стека уже как-то не хочется.

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Просто для фана. Похоже, что стэк всё равно раскручивается назад. Пример взят с одной лекции по исключениям (там тоже написано, что так делать не надо :) )

»
11 лет назад, # |
Rev. 2   Проголосовать: нравится +8 Проголосовать: не нравится

В спойлере без изменения первой функции.

»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Вот :)

#include <cstdio>

void f1()
  {
  float temp;
  sscanf("12.8", "%f", &temp);
  }

float f3()
  {
  float temp;
  sscanf("", "%f", &temp);
  return temp;
  }

void f2()
  {
  float temp;
  f1();
  printf("%f\n", f3());
  }

int main(void)
  {
  f2();
  return 0;
  }

Понятно, что способ считывания не важен. В f3 sscanf нужен чтобы компилятор не понял, что переменная не инициализируется.

»
11 лет назад, # |
  Проголосовать: нравится +4 Проголосовать: не нравится

Здравствуйте.

Я этот идиот\автор этой задачи. Она мной не придумана с нуля. На 4 курсе была курсовая на С++, где некоторые функции были целиком написаны на асме. В один момент пришлось посмотреть полностью ассемблерный листинг в отладчике, там я заметил кое-что забавное, отсюда и идея загадки.

Вы наворотили тут просто горы всякой лишней ерунды. Нет, мне лестно, конечно! Но вы чересчур усложняете.

Задача обрабатывала вещественные числа. Только double, а не float, как я указал в условии — это мой промах за давностью. То есть не 4 байта, а 8. В задаче использовались инструкции математического сопроцессора — FPU. В 32-битном режиме оператор return функции типа double заталкивает квадрослово в стек сопроцессора. (В 64-битном — для этого можно использовать RAX, верно. Но изначально режим был 32-битный.) В моем случае оно (квадрослово со значением) было там изначально. В листинге это выглядело так: некая обработка чисел в fpu-стеке — затем выталкиваем верхнее значение в переменную result — затем оператор return заталкивает его обратно, затем во второй функции на месте, где было что-то типа variable = func1() топ стека опять выталкивается в переменную variable. Что я тогда сделал? Мне не понравилась такая избыточность, я убрал return и переменную result. Стал оставлять значение как есть — в верхушке стека сопроцессора, а во второй функции вместо variable = func1() написал __asm fstp qword ptr [variable]. А поскольку компилятор ругнулся на отсутствие ретурна я, вместо того, чтобы прагмой отключать ругань — просто изменил тип функции на void. Все.

Это задачка скорей на reverse engineering, чем на придумывание всяких нагромождений из try\catch, каких-то деревьев и прости-господи что тут еще было. =) Благодарю за внимание.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    Вот фрагмент кода из курсовой.

    void asm_min ( double r, double g, double b ) {
    __asm
    {
    fld r
    fld g
    fld b
    fcomi st(0),st(1)
    fcmovnb st(0),st(1)
    fstp st(1)
    fcomi st(0),st(1)
    fcmovnb st(0),st(1)
    fstp st(1)
    }
    }
    //здесь просто ищется вещественный минимум средствами FPU
    
    //где-то в другой функции... в другой инлайн-вставке...
    push dword ptr [rgb.b+4]
    push dword ptr [rgb.b]
    push dword ptr [rgb.g+4]
    push dword ptr [rgb.g]
    push dword ptr [rgb.r+4]
    push dword ptr [rgb.r] //заталкиваем аргументы функции в "общий" стек
    call [asm_min] //вызываем функцию
    add esp, 8*3 //выталкиваем мусор из "общего" стека путем передвигания его указателя назад на три байта
    fstp qword ptr [minimal] //инструкция - выталкивает верхнее значение стека FPU, размером в квадрослово, в область памяти по адресу [minimal]
    
  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится

    А где вы увидели решение с деревьями?

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится +55 Проголосовать: не нравится

      профессиональные программисты, увидев слово Tree, обычно сразу бросают читать этот фрагмент и сразу переходят к комментированию.

    • »
      »
      »
      11 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится -22 Проголосовать: не нравится

      Да там... где про ветки рекурсии, сыновей, листья и правое поддерево.)

      Верно, верно, это не решение задачи. Я не читал все полностью, пробежал взглядом по диагонали — ужас какой.

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится +6 Проголосовать: не нравится

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

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится +18 Проголосовать: не нравится

        Более того, у меня большие сомнения, что Вы знаете, что такое try/catch.

        Вот интересно, а Вы можете предложить решение этой задачи для int?

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится -6 Проголосовать: не нравится

          Мне, имея в голове простейшее решение, было забавно читать и про try\catch. И даже про "взлом стека". Нет, этот вариант мне понравился, если бы все было сложно, то он был бы крут. Но тут-то все просто.

          (А уж про другие нагромождения кода, написанные с нуля, вообще молчу, т.к. их авторы явно не читали условия полностью — где говорится, что в качестве шаблона должен быть взят заданный фрагмент кода и дописываться что-то должно только в теле имеющихся двух функций. Но я никого не хочу обидеть. Примеры несомненно годные и толковые, жаль только что я, наскоро и по диагонали просматривая комментарии — честно, я и впрямь их не читал ни все, ни полностью — не въехал в тему, что здесь обсуждается еще и поиск в бинарном дереве. Ну, не ожидал я его здесь увидеть, да и все. %) )

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится +26 Проголосовать: не нравится

            Ваше "простейшее решение" не является решением задачи, например, потому что приведённый вами выше "фрагмент кода из курсовой" не является корректным фрагментом кода на C++ (надеюсь, "после пяти курсов университета по специальности КН и СИИ" вы знаете, что в C++ нет конструкции "__asm {...}", это всего лишь фича майкрософтского компилятора?).

            Не говоря уж о том, что в задаче в посте не были ни слова о 32битах или вообще о том, под какую платформу это будет собираться, так что самое нормальное решение — throw + try/catch.

            • »
              »
              »
              »
              »
              »
              »
              11 лет назад, # ^ |
                Проголосовать: нравится +19 Проголосовать: не нравится

              Да и задача эта была... скажем так, просто загадкой. Я, простите, загадывая ее товарищу BorN'у совсем на другом ресурсе, ожидал каких-то измышлений от него лично, но не рассчитывал на то, что она будет явлена миру as is. Если бы я сочинял условие для этого ресурса — я бы, конечно, подошел к делу ответственней, чем загадывая кодерскую загадку для поддавана в "свободных темах" на ролевом форуме. )))

              • »
                »
                »
                »
                »
                »
                »
                »
                11 лет назад, # ^ |
                  Проголосовать: нравится -21 Проголосовать: не нравится

                Насколько я знаю, товарищ BorN в 11 классе. Не Думаю что он мог подумать о асемблерных вставках и решить вашу задачу самостоятельно,

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  11 лет назад, # ^ |
                    Проголосовать: нравится +2 Проголосовать: не нравится

                  Зря Вы такого низкого мнения о школьниках.

                  Когда я учился еще в 10 классе, мне удалось сломать триальную версию эмулятора калькулятора Casio FX-9860G II. Для этого потребовалось с помощью IDA запустить экзешник в режиме отладки, найти нужную проверку и заменить ее на безусловный переход. Причем, для выполнения последнего пункта пришлось напрямую редактировать машинный код в HEX-редакторе.

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

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
            Rev. 3   Проголосовать: нравится +40 Проголосовать: не нравится

            Я не троллю, я не умею. Была сформулирована задача на "Я.п. с++". Было предложено решение в рамках этого языка (try/catch). Все нормально, симпатично.

            Вдруг появляется "Вы наворотили тут просто горы всякой лишней ерунды", с предложением заценить какую-то догадку в ассембленом коде ("простейшее решение"). Причем, зависящее от платформы.

            Мне не понравилось.

            • »
              »
              »
              »
              »
              »
              »
              11 лет назад, # ^ |
              Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится

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

              Я был несколько шокирован их количеством, при этом зная свой путь. Уже пройденный. Из двух или трех строк. Да и вообще удивлен тем, что мой пост опубликован здесь без изменений.

              Проще говоря, а мне не понравилось, что мой пост опубликован здесь as is. Меня выставили на посмешище, т.к. вздумай я публиковать эту, в общем-то, не претендующую на особенную загадочность темку здесь, в обществе действительно знающих и продвинутых людей — я бы, повторюсь, подошел к делу ответственней и учел бы максимум детали. Да и вряд ли стал бы это делать — т.к. здесь она действительно не смотрится, слишком проста. Она была рассчитана на несколько иной уровень. Отсюда и несколько вспыльчивая реакция. С ув,

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
            Rev. 4   Проголосовать: нравится 0 Проголосовать: не нравится

            > А уж про другие нагромождения кода, написанные с нуля, вообще молчу, т.к. их авторы явно не читали условия полностью — где говорится, что в качестве шаблона должен быть взят заданный фрагмент кода и дописываться что-то должно только в теле имеющихся двух функций.

            Если это про мой вариант — то весьма необоснованно. Я читал условие, но показал идею, а не конкретно то, что надо написать. Да, я заменил cin на sscanf, чтобы можно было продемонстрировать работу кода в codepad'е. И Добавил функцию, чтобы было понятнее, что происходит. Вот всё то же самое, но в точности в соответствии с требованиями:

            #include <iostream>
            #include <cstdio>
            
            using namespace std;
             
            void func1() {
              float temp;
              cin >> temp;
            }
             
            void func2() {
              float temp;
              func1();
              temp = [](){float temp; sscanf("", "%f", &temp); return temp;}();
              cout << temp;
            }
            
            int main() {
              func2();
              return 0;
            }
            

            Компилируется и работает в VS2012 в режиме Release. Кстати, в этом решении тип переменной по сути не важен.

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится +21 Проголосовать: не нравится

        Вы не заметили мое решение(в спойлере), оно как раз делает, то что вы описали, копирую еще раз http://pastebin.com/b5MaPTR9.

        Не заметили решения user:dalex и user:andreyv, которые еще круче особенно последнее, без всякого ассемблера.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Вы преподаватель, а автор темы — ваш ученик, так?

    Я прочитал историю. Получается, что то, что нагенерировал компилятор, было не очень оптимальным, поэтому возник такой говнокод?

    Я бы все-таки хотел — где угодно — что в курсовой работе, что в олимпиадном коде, что в рабочем проекте — видеть все-таки variable = func1(), а не __asm fstp qword ptr [variable].

»
11 лет назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится

На самом деле, мне хотелось бы извиниться за нелестные высказывания в самом начале — про "нагородили ерунды" и т.п. Простите, ляпнул сгоряча, не разобравшись. На трезвую голову было интересно почитать все, что тут насочиняли.