Помогите справиться с заданием. Идей нету вообще. Заранее спасибо
Условие:
Я.п. с++. Есть две подфункции одного уровня. Func1 и func2. Обе функции типа void, не получают и не возвращают значений. В обеих функциях есть локальная переменная temp. Функция func2 вызывает func1, а затем выводит строку со значением своей переменной temp на экран. Вот код
Задача:
функция func2 должна выводить значение, полученное от пользователя функцией func1 (то есть она должна каким-то образом получить это значение, для этого в ней и зарезервирована своя переменная temp, для удобства). Для реализации этого допускаются любые программно-аппаратные средства, КРОМЕ:
Изменения типов функций. Они должны оставаться void и не должны ни к чему кастоваться при вызове. Строго говоря, ни в одной из функций не должна находиться инструкция return.
Переноса инструкции cin >> temp из первой функции во вторую. Или добавление второй такой инструкции в func2. Значение должно запрашиваться один раз и только из функции func1. Выводиться это значение обязательно, соответственно, должно непременно из функции func2.
Использования глобальных переменных. А также всяческих статических классов и прочей ерунды. Добавляться в ходе решения что-то должно только в коде этих двух функций.
Использования указателей, ссылок.
Записи\чтения файлов.
Использования системных средств — семафоров, счетчиков, флажков, блокировок и т.д. А также любых других средств API.
Использования сетевых соединений
А откуда задача?
сам не знаю. мне один знакомый подсунул
Похоже, что в задаче требуется грамотно поломать стек)
Без указателей не сможешь :).
Easy:
http://pastebin.com/Zi1YtW47
Это только в MS C++ работает. Можно в запуске проверить.
Использование регистра EAX в качестве глобальной переменной? :)
Why not?
Легко можно: в
func1
делаем что-то типаКонечно, при условии, что с выравниванием всё ок.
Использовали ли Вы указатели? Строго говоря, да (arr является указателем на начало зарезервированного в стеке блока памяти). А что об этом думает тот
идиотпрепод, который дал задачу?Использовал ли dalex глобальные переменные? Строго говоря, регистр процессора можно считать глобальной переменной. А что думает препод?
Вот из-за этой субъективности задачи подобного рода меня страшно бесят.
Оу, func1 и func2 это тоже указатели. Занудство)
Если уж быть занудой до конца, в строчке
cin >> temp;
происходит передача аргумента по ссылке в операторную функцию.А к чему я это? А к тому, что ограничения наложены на код, который мы пишем сами, а не на то, что изначально написано.
Александр, вы, кажется, в основном пишете на яве, так что вам простительно. Указатель и массив с точки зрения C++ различные типы данных, так что объявление float arr[1]; не использует указатели.
Мне пришлось всё-таки признать, что он прав. Я посмотрел, оператор квадратных скобок ожидает указатель как один из операндов, поэтому в
arr
сначала будет преобразован в указатель, и только потом выполнится оператор[]
.Хак: http://pastie.org/8611221
Это намного лучше, чем у меня.
Правда, вызов func1() находится внутри try, что вроде не соответствует заданию.
Круто:))
Есть такой трюк. Ещё один юз:
А какой смысл этого трюка? Что, try/catch работает быстрее return?
мб все остальные ветки рекурсии автоматически обрубаются?
А при return что, не обрубаются?
не должны. я встал в какую-то вершину. спустился из нее в правого сына и в левого. в правом внезапно нашел строку s и return-ул ее. но из левого сына я буду продолжать спускаться до листьев. другое дело, что было бы логично, если однажды в ходе программы функция fnd() уже облажалась, сразу все ее вызовы во всех местах из стека потереть.
Если что-то нашел в правом поддереве, значит, левое уже было полностью обработано.
Какая разница, по какой инструкции потереть из стека вызовы, по return или throw?
Раскрой уже нормальный акк.
if (p->left) fnd(p->left, s)
. но от этого не перестанет существовать(p->right)
. и я все равно пойду в него. другое дело, что если я что-то кинул в какой-то вершине дерева вызовов, то все дерево (maybe) потрется.Все, я понял, что имеется в виду, я сам немного затупил, претензий больше не имею
Смысл трюка в том, чтобы сделать неочевидную и медленно работающую конструкцию.
Так и хотел написать: "Люди, никогда так не пишите!".
А с чего бы эта конструкция медленно работала, кстати? В варианте без кидания исключения будет лишняя проверка возвращаемого значения fnd(p->left, s)
Казалось бы, это давно известный факт, что обработка исключений дает огромный overhead.
Однако, бенчмарк (DFS на вырожденных в цепочку деревьях) выявил весьма интересную вещь: при размере цепочки до примерно 6500 версия с if-ом работает быстрее, на больших тестах она начинает проигрывать версии с исключениями. Видимо, причина в быстром сворачивании стека.
Я сейчас ради интереса напишу версии с setjmp/longjmp и с ручным сворачиванием стека.
UPD. Написал с setjmp/longjmp, обходит версию с if-ом начиная где-то с 22 вершин.
UPD2. Исходя из результата бенчмарка с setjmp/longjmp, писать ручное сворачивание стека уже как-то не хочется.
Просто для фана. Похоже, что стэк всё равно раскручивается назад. Пример взят с одной лекции по исключениям (там тоже написано, что так делать не надо :) )
В спойлере без изменения первой функции.
Вот :)
Понятно, что способ считывания не важен. В f3 sscanf нужен чтобы компилятор не понял, что переменная не инициализируется.
Здравствуйте.
Я этот идиот\автор этой задачи. Она мной не придумана с нуля. На 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, каких-то деревьев и прости-господи что тут еще было. =) Благодарю за внимание.
Вот фрагмент кода из курсовой.
А где вы увидели решение с деревьями?
профессиональные программисты, увидев слово Tree, обычно сразу бросают читать этот фрагмент и сразу переходят к комментированию.
Да там... где про ветки рекурсии, сыновей, листья и правое поддерево.)
Верно, верно, это не решение задачи. Я не читал все полностью, пробежал взглядом по диагонали — ужас какой.
Ваш ответ показывает, что вы даже не читали комментария целиком, или не вникали в него. После того становиться грустно читать ваш пост в вк про то, как "лучшие умы интернета", наворотили таких решений по вашей простой задаче. Вы в этих решениях, по всей видимости, даже не разбирались, раз увидели в коде поиска в бинарном дереве решение своей задачи.
Более того, у меня большие сомнения, что Вы знаете, что такое try/catch.
Вот интересно, а Вы можете предложить решение этой задачи для int?
Мне, имея в голове простейшее решение, было забавно читать и про try\catch. И даже про "взлом стека". Нет, этот вариант мне понравился, если бы все было сложно, то он был бы крут. Но тут-то все просто.
(А уж про другие нагромождения кода, написанные с нуля, вообще молчу, т.к. их авторы явно не читали условия полностью — где говорится, что в качестве шаблона должен быть взят заданный фрагмент кода и дописываться что-то должно только в теле имеющихся двух функций. Но я никого не хочу обидеть. Примеры несомненно годные и толковые, жаль только что я, наскоро и по диагонали просматривая комментарии — честно, я и впрямь их не читал ни все, ни полностью — не въехал в тему, что здесь обсуждается еще и поиск в бинарном дереве. Ну, не ожидал я его здесь увидеть, да и все. %) )
Ваше "простейшее решение" не является решением задачи, например, потому что приведённый вами выше "фрагмент кода из курсовой" не является корректным фрагментом кода на C++ (надеюсь, "после пяти курсов университета по специальности КН и СИИ" вы знаете, что в C++ нет конструкции "__asm {...}", это всего лишь фича майкрософтского компилятора?).
Не говоря уж о том, что в задаче в посте не были ни слова о 32битах или вообще о том, под какую платформу это будет собираться, так что самое нормальное решение — throw + try/catch.
Да и задача эта была... скажем так, просто загадкой. Я, простите, загадывая ее товарищу BorN'у совсем на другом ресурсе, ожидал каких-то измышлений от него лично, но не рассчитывал на то, что она будет явлена миру as is. Если бы я сочинял условие для этого ресурса — я бы, конечно, подошел к делу ответственней, чем загадывая кодерскую загадку для поддавана в "свободных темах" на ролевом форуме. )))
Насколько я знаю, товарищ BorN в 11 классе. Не Думаю что он мог подумать о асемблерных вставках и решить вашу задачу самостоятельно,
Зря Вы такого низкого мнения о школьниках.
Когда я учился еще в 10 классе, мне удалось сломать триальную версию эмулятора калькулятора Casio FX-9860G II. Для этого потребовалось с помощью IDA запустить экзешник в режиме отладки, найти нужную проверку и заменить ее на безусловный переход. Причем, для выполнения последнего пункта пришлось напрямую редактировать машинный код в HEX-редакторе.
Я все это говорю к тому, что не надо выставлять ассемблер как что-то принципиально сложное и недоступное для самостоятельного изучения.
Я не троллю, я не умею. Была сформулирована задача на "Я.п. с++". Было предложено решение в рамках этого языка (try/catch). Все нормально, симпатично.
Вдруг появляется "Вы наворотили тут просто горы всякой лишней ерунды", с предложением заценить какую-то догадку в ассембленом коде ("простейшее решение"). Причем, зависящее от платформы.
Мне не понравилось.
ОК, я признаю свою горячность. Некоторые из предложенных решений действительно имеют равное право на существование. В других условиях.
Я был несколько шокирован их количеством, при этом зная свой путь. Уже пройденный. Из двух или трех строк. Да и вообще удивлен тем, что мой пост опубликован здесь без изменений.
Проще говоря, а мне не понравилось, что мой пост опубликован здесь as is. Меня выставили на посмешище, т.к. вздумай я публиковать эту, в общем-то, не претендующую на особенную загадочность темку здесь, в обществе действительно знающих и продвинутых людей — я бы, повторюсь, подошел к делу ответственней и учел бы максимум детали. Да и вряд ли стал бы это делать — т.к. здесь она действительно не смотрится, слишком проста. Она была рассчитана на несколько иной уровень. Отсюда и несколько вспыльчивая реакция. С ув,
> А уж про другие нагромождения кода, написанные с нуля, вообще молчу, т.к. их авторы явно не читали условия полностью — где говорится, что в качестве шаблона должен быть взят заданный фрагмент кода и дописываться что-то должно только в теле имеющихся двух функций.
Если это про мой вариант — то весьма необоснованно. Я читал условие, но показал идею, а не конкретно то, что надо написать. Да, я заменил cin на sscanf, чтобы можно было продемонстрировать работу кода в codepad'е. И Добавил функцию, чтобы было понятнее, что происходит. Вот всё то же самое, но в точности в соответствии с требованиями:
Компилируется и работает в VS2012 в режиме Release. Кстати, в этом решении тип переменной по сути не важен.
Вы не заметили мое решение(в спойлере), оно как раз делает, то что вы описали, копирую еще раз http://pastebin.com/b5MaPTR9.
Не заметили решения user:dalex и user:andreyv, которые еще круче особенно последнее, без всякого ассемблера.
Вы преподаватель, а автор темы — ваш ученик, так?
Я прочитал историю. Получается, что то, что нагенерировал компилятор, было не очень оптимальным, поэтому возник такой говнокод?
Я бы все-таки хотел — где угодно — что в курсовой работе, что в олимпиадном коде, что в рабочем проекте — видеть все-таки
variable = func1()
, а не__asm fstp qword ptr [variable]
.На самом деле, мне хотелось бы извиниться за нелестные высказывания в самом начале — про "нагородили ерунды" и т.п. Простите, ляпнул сгоряча, не разобравшись. На трезвую голову было интересно почитать все, что тут насочиняли.