Всем добрый день! Почему в данном коде не получается перейти от одной функций к другой методом goto? Компилятор пишет что функции bar, fellodd и Elochka не определены.Даже если удалось запустить программу, она завершается без выполнения данной функций. Где то даже читал что это невозможно сделать. (По задумке функций должны были проигрывать определенные мелодии(Beep). Многоточия-замена части кода, не имеющей отношения к проблеме.
void bar( ); void feelgood( ); void elochka( ); void improv( ); int main() < . k=getch(); switch (k) < case 1 : goto bar(); case 2 : goto feelgood(); case 3 : goto elochka(); default : return 0; >// switch return 0; >// main void elochka() < . >void bar( ) < . >void feelgood( )
Отслеживать
30.9k 13 13 золотых знаков 96 96 серебряных знаков 157 157 бронзовых знаков
задан 10 мая 2017 в 12:15
31 1 1 серебряный знак 9 9 бронзовых знаков
Ну наверно начну с самого распространенного ответа в таком случае — не используйте goto НИКОГДА ! По сабжу — а где ваш .h файл ? Именно в нем определяются функции и наверняка именно поэтому ругается компилятор
5 Простых и КРУТЫХ Переходов для ВИДЕО | Как Монтировать НА ТЕЛЕФОНЕ для TikTok, Instagram и Youtube
10 мая 2017 в 12:17
Это не полный код. вырезал их потому что тут отображаются некорректно. Были подключены #include #include #include
10 мая 2017 в 12:18
И чем goto настолько плох? И есть ему замена в данном случае?
10 мая 2017 в 12:20
10 мая 2017 в 12:21
А что сделано не так?Даже если убрать goto, оставив просто вызов функций, то она все равно не выполняется
10 мая 2017 в 12:24
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Из стандарта языка C (6.8.1 Labeled statements)
1 labeled-statement: identifier : statement
3 Label names shall be unique within a function.
И, наконец, относительно goto предложения (6.8.6.1 The goto statement)
2 A goto statement causes an unconditional jump to the statement prefixed by the named label in the enclosing function.
То есть вы можете использовать предложение goto в пределах одной функции, причем передавать управление на предложение, которому предшествует идентификатор, за которым следует двоеточие.
Очевидно, что данное предложение
goto bar();
по крайней мере синтаксически неверно, так как bar() не является меткой. А самое главное — вы не можете с помощью goto перескочить из одной функции в другую.
Похоже, все, что вам требуется, это просто вызвать соответствующие функции. Вы можете это сделать следующим образом.
switch (k)
или в зависимости от типа и значения, хранимого в переменной k
Источник: ru.stackoverflow.com
7.6 – Операторы goto
Следующий тип инструкций управления порядком выполнения программы, который мы рассмотрим, – это безусловный переход. Безусловный переход заставляет выполнение перемещаться в другое место в коде. Термин «безусловный» означает, что переход происходит всегда (в отличие от оператора if или оператора switch , где переход происходит только на основе результата условного выражения).
10 Простых Переходов, Которые Должен Знать Каждый / Видео Переходы Для Монтажа
В C++ безусловные переходы реализуются с помощью инструкции goto , а точка перехода определяется с помощью метки инструкции. Ниже приведен пример инструкции goto и метки инструкции:
#include #include // для функции sqrt() int main() < double x<>; tryAgain: // это метка инструкции std::cout > x; if (x < 0.0) goto tryAgain; // это инструкция goto std::cout
В этой программе пользователя просят ввести неотрицательное число. Однако если введено отрицательное число, программа использует инструкцию goto для возврата к метке tryAgain . Затем пользователя снова просят ввести новое число. Таким образом, мы можем постоянно просить пользователя ввести данные, пока он или она не введет что-то допустимое.
Вот пример вывода при запуске этой программы:
Enter a non-negative number: -4 Enter a non-negative number: 4 The square root of 4 is 2
Метки инструкций имеют область видимости
В главе, посвященной области видимости объектов (глава 6), мы рассмотрели три вида области видимости: локальную область видимости (область видимости бллока), область видимости файла и глобальную область видимости. Метки инструкций используют четвертый вид области видимости: область видимости функции, что означает, что метка видна во всей функции даже до точки ее объявления. Инструкция goto и соответствующая ей метка инструкции должны находиться в одной функции.
Хотя в приведенном выше примере показана инструкция goto , которая выполняет переход назад (к предыдущей точке в функции), инструкции goto могут перемещаться и вперед:
#include void printCats(bool skip) < if (skip) goto end; // переход вперед; метка инструкции ‘end’ видна здесь, // так как имеет область видимости функции std::cout int main() < printCats(true); // перепрыгнуть через инструкцию печати и ничего не печатать printCats(false); // напечатать»cats» return 0; >
Эта программа напечатает:
cats
Помимо перехода вперед, в приведенной выше программе стоит упомянуть еще несколько интересных вещей.
Во-первых, обратите внимание, что метки инструкций должны быть связаны с инструкциями (отсюда и их название: они помечают инструкцию). Поскольку в конце функции не было инструкции, нам пришлось использовать пустую инструкцию, чтобы у нас было что пометить. Во-вторых, мы смогли перейти к инструкции, помеченной как end , хотя мы даже еще не объявили end , это потому, что метки инструкций имеют область видимости функции. Предварительное объявление меток инструкций не требуется. В-третьих, стоит прямо упомянуть, что приведенная выше программа – плохой пример: было бы лучше использовать оператор if , чтобы пропустить инструкцию печати, а не оператор goto , чтобы перепрыгнуть через нее.
У переходов есть два основных ограничения: вы можете выполнять переходы вперед или назад только в рамках одной функции (вы не можете перепрыгивать из одной функции в другую), и если вы выполняете переход вперед, вы не можете перескочить вперед через инициализацию любой переменной, которая находится в области видимости в месте перехода. Например:
int main() < goto skip; // ошибка: этот переход недопустим, потому что. int x < 5 >; // эта инициализированная переменная все еще находится // в области видимости в метке инструкции ‘skip’ skip: x += 3; // как это должно было бы вычисляться, // если бы x не была инициализирована? return 0; >
Обратите внимание, что вы можете выполнить переход назад через инициализацию переменной, и при выполнении инициализации переменная будет повторно инициализирована.
Избегайте использования goto
Использование goto в C++ (а также в других современных высокоуровневых языках) не допускается. Эдсгер В. Дейкстра, известный ученый-информатик, изложил аргументы в пользу отказа от goto в известной, но трудной для чтения статье под названием «Go To Statement Considered Harmful». Основная проблема с goto заключается в том, что он позволяет программисту произвольно перемещаться по коду. Это создает то, что не очень ласково называют спагетти-кодом. Спагетти-код – это код, порядок выполнения которого напоминает тарелку спагетти (все запутанные и скрученные), что чрезвычайно затрудняет отслеживание логики такого кода.
Как с юмором говорит Дейкстра, «качество программистов – это функция, обратно пропорциональная плотности инструкций переходов в создаваемых ими программах».
Практически любой код, написанный с помощью операторов goto , можно более четко написать с использованием других конструкций C++, таких как операторы if и циклы. Одно примечательное исключение – это когда вам нужно выйти из вложенного цикла, но не из всей функции – в таком случае переход сразу за пределы циклов, вероятно, является самым чистым решением.
Лучшая практика
Избегайте операторов goto (если альтернативы значительно не ухудшают читаемость кода).
Источник: radioprog.ru
Переходы
Как уже отмечалось в гл. 1, присущий процессору алгоритм выполнения программы заставляет его выполнять команды программы друг за другом, в том порядке, как они были описаны в исходном тексте программы и содержатся в выполнимом модуле.
Однако часто программисту требуется нарушить этот порядок, заставив процессор обойти некоторый участок программы, перейти на выполнение другой ветви или передать управление подпрограмме, имея в виду после ее завершения вернуться на прежнее место. Все эти операции осуществляются с помощью команд переходов. Переходы разделяются на безусловные, когда передача управления в другую точку программы осуществляется в безусловном порядке, независимо ни от каких обстоятельств, и условные, осуществляемые или не осуществляемые в зависимости от тех или иных условий: результатов сравнения, анализа, поиска и т.п. Безусловные переходы подразделяются на собственно переходы (без возврата в точку перехода) и вызовы подпрограмм (с возвратом после завершения подпрограммы).
Операции переходов и вызовов подпрограмм, помимо их практической ценности, представляют значительный методический интерес, так как затрагивают основу архитектуры процессора — сегментную адресацию памяти. Многочисленные разновидности команд переходов и вызовов обязаны своим существованием не столько потребностям практического программирования, сколько принципиальным архитектурным особенностям процессора. Отчетливое понимание этих особенностей и, соответственно, условий применения и возможностей различных операций переходов необходимо не только при использовании языка ассемблера, но и при программировании на языках высокого уровня, где иногда используется несколько иная терминология, но существо дела остается без изменения.
Безусловные переходы осуществляются с помощью команды jmp, которая может использоваться в 5 разновидностях. Переход может быть:
прямым коротким (в пределах -128. + 127 байтов);
прямым ближним (в пределах текущего сегмента команд):
прямым дальним (в другой сегмент команд);
косвенным ближним (в пределах текущего сегмента команд через ячейку
с адресом перехода);
косвенным дальним (в другой сегмент команд через ячейку с адресом
перехода).
Рассмотрим последовательно структуру программ с переходами разного вида.
Прямой короткий (short) переход. Прямым называется переход, в команде которого в явной форме указывается метка, на которую нужно перейти. Разумеется, эта метка должна присутствовать в том же программном сегменте, при этом помеченная ею команда может находиться как до, так и после команды jmp.
Достоинство команды короткого перехода заключается в том, что она занимает лишь 2 байт памяти: в первом байте записывается код операции (EBh), во втором — смещение к точке перехода. Расстояние до точки перехода отсчитывается от очередной команды, т.е. команды, следующей за командой jmp. Поскольку требуется обеспечить переход как вперед, так и назад, смещение рассматривается, как число со знаком и, следовательно, переход может быть осуществлен максимум на 127 байт вперед или 128 байт назад. Прямой короткий переход оформляется следующим образом:
Если программа транслируется ассемблером TASM, и в строке вызова транслятора указано, что трансляцию следует выполнить в два прохода
то описатель short можно опустить, так как ассемблер сам определит, что расстояние до точки перехода укладывается в короткий переход, даже если метка go расположена после строки с командой jmp. При использовании транслятора MASM указание описателя short обязательно (если метка go расположена после команды jmp). Здесь проявляются незначительные различия ассемблеров разных разработчиков.
В комментарии указан код команды; dd (от displacement, смещение) обозначает байт со смещением к точке перехода от команды, следующей за командой jmp.
При выполнении команды прямого короткого перехода процессор прибавляет значение байта dd к младшему байту текущего значения указателя команд IP (который, как уже говорилось, всегда указывает на команду, следующую за выполняемой). В результате в IP оказывается адрес точки перехода, а предложения, находящиеся между командой jmp и точкой перехода, не выполняются. Между прочим, конструкция с прямым переходом вперед часто используется для того, чтобы обойти данные, которые по каким-то причинам желательно разместить в сегменте команд.
Прямой ближний (near), или внутрисегментный переход. Этот переход отличается от предыдущего только тем, что под смещение к точке перехода отводится целое слово. Это дает возможность осуществить переход в любую точку 64-кбайтного сегмента.
Метка go может находиться в любом месте сегмента команд, как до, так и после команды jmp. В коде команды dddd обозначает слово с величиной относительного смещения к точке перехода от команды, следующей за командой jmp.
При выполнении команды прямого ближнего перехода процессор должен прибавить значение слова dddd к текущему значению указателя команд IP и сформировать тем самым адрес точки перехода. Что представляет собой смещение ddddl Какая это величина, со знаком или без знака? Если рассматривать смещение как величину без знака, то переход будет возможен только вперед, что, конечно, неверно. Если же смещение является величиной со знаком, то переход возможен не более, чем на полсегмента вперед или на полсегмента назад, что тоже неверно. В действительности, рассматривая вычисление адреса точки перехода, следует иметь в виду явление оборачивания, суть которого можно кратко выразить такими соотношениями:
Если последовательно увеличивать содержимое какого-либо регистра или ячейки памяти, то, достигнув верхнего возможного предела FFFFh, число «перевалит» через эту границу, станет равным нулю и продолжит нарастать в области малых положительных чисел (1, 2, 3, и т.д.). Точно так же, если последовательно уменьшать некоторое положительное число, то оно, достигнув нуля, перейдет в область отрицательных (или, что то же самое, больших беззнаковых) чисел, проходя значения 2, 1, 0, FFFFh, FFFEh и т.д.
Таким образом, при вычислении адреса точки перехода смещение следует считать числом без знака, но при этом учитывать явление оборачивания. Если команда jmp находится где-то в начале сегмента команд, а смещение имеет величину порядка 64 К, то переход произойдет вперед, к концу сегмента. Если же команда находится в конце сегмента команд, а смещение имеет ту же величину порядка 64 К, то для определения точки перехода надо двигаться по сегменту вперед, дойти до его конца и продолжать перемещаться от начала сегмента по-прежнему вперед, пока не будет пройдено заданное в смещении число байтов. Для указанных условий мы попадем в точку, находящуюся недалеко от команды jmp со стороны меньших адресов.
Итак, с помощью команды ближнего перехода (команда jmp без каких-либо спецификаторов) можно перейти в любую точку в пределах данного сегмента команд. Для того, чтобы перейти в другой сегмент команд, следует воспользоваться командой дальнего перехода.
Прямой дальний (far), или межсегментный переход. Этот переход позволяет передать управление в любую точку любого сегмента. При этом, очевидно, предполагается, что программный комплекс включает несколько сегментов команд. Команда дальнего перехода имеет длину 5 байт и включает, кроме кода операции EAh, еще и полный адрес точки перехода, т.е. сегментный адрес и смещение.
Транслятору надо сообщить, что этот переход — дальний (по умолчанию команда jmp транслируется, как команда ближнего перехода). Это делается с помощью описателя far ptr, указываемого перед именем точки перехода.
codel segment
assume CS: codel ;Сообщим транслятору, что это сегмент команд
…
jmp far ptr go ;Код EA dddd ssss
…
codel endscode2 segment
assume CS : code2 ; Сообщим транслятору, что это сегмент команд
…
gо:
…
code2 ends
Метка go находится в другом сегменте команд этой двухсегментной программы. В коде команды ssss — сегментный адрес сегмента code2, a dddd — смещение точки перехода go в сегменте команд code2.
Заметим, что при наличии в программе нескольких сегментов команд, каждый из них необходимо предварять директивой ассемблера assume СS:имя_сегмента, которая сообщает транслятору о том, что начался очередной сегмент команд. Это поможет транслятору правильно обрабатывать адреса меток, встречающихся в этом сегменте.
Освоив применение команд дальних переходов, мы получили возможность создавать программы любой длины. Действительно, предусмотрев в конце каждого программного сегмента команду дальнего перехода на начато следующего, мы можем включить в программный комплекс любое число сегментов по 64 Кбайт. Единственное ограничение — чтобы они все поместились в памяти.
В действительности так, конечно, не делают. Разумнее дополнительные сегменты команд заполнить подпрограммами и вызывать их из основного сегмента (или друг из друга) по мере необходимости. Однако и в этом случае команды вызовов подпрограмм должны быть дальними. Разновидности подпрограмм и команд их вызова будут рассмотрены ниже.
Все виды прямых переходов требуют указания в качестве точки перехода программной метки. С одной стороны, это весьма наглядно; просматривая текст программы, можно сразу определить, куда осуществляется переход. С другой стороны, такой переход носит статический характер — его нельзя настраивать по ходу программы.
Еще более серьезный недостаток прямых переходов заключается в том, что они не дают возможность перейти по известному абсолютному адресу, т.е. не позволяют обратиться ни к системным средствам, ни вообще к другим загруженным в память программам (например, резидентным). Действительно, программы операционной системы не имеют никаких меток, так как метка — это атрибут исходного текста программы, а программы операционной системы транслировались не нами и присутствуют в компьютере только в виде выполнимых модулей. А вот адреса каких-то характерных точек системных программ определить можно, хотя бы из векторов прерываний. Для обращения по абсолютным адресам надо воспользоваться командами косвенных переходов, которые, как и прямые, могут быть ближними и дальними.
Косвенный ближний (внутрисегментный) переход. В отличие от команд прямых переходов, команды косвенных переходов могут использовать различные способы адресации и, соответственно, иметь много разных вариантов. Общим для них яштястся то, что адрес перехода не указывается явным образом в виде метки, а содержится либо в ячейке памяти, либо в одном из регистров.
Это позволяет при необходимости модифицировать адрес перехода, а также осуществлять переход по известному абсолютному адресу. Рассмотрим случай, когда адрес перехода хранится в ячейке сегмента данных. Если переход ближний, то ячейка с адресом состоит из одного слова и содержит только смещение к точке перехода.
code segment
…
jmp DS:go_addr ;Код FF 26 dddd
…
go: ; Точка перехода
…
code endsdata segment
…
go_addr dw go ;Адрес перехода (слово)
…
data ends
Точка перехода go может находиться в любом месте сегмента команд. В коде команды dddd обозначает относительный адрес слова go_addr в сегменте данных, содержащем эту ячейку.
В приведенном фрагменте адрес точки перехода в слове go_addr задан однозначно указанием имени метки go. Такой вариант косвенного перехода выполняет фактически те же функции, что и прямой (переход по единственному заданному заранее адресу), только несколько более запутанным образом. Достоинства косвенного перехода будут более наглядны, если представить, что ячейка go_addr поначалу пуста, а по ходу выполнения программы в нес, в зависимости от каких-либо условий, помещается адрес той или иной точки перехода:
mov go_addr, offset gol
mov go_addr, offset go2
mov go_addr, offset go3
Разумеется, приведенные выше команды должны выполняться не друг за другом, а альтернативно. В этом случае создается возможность перед выполнением перехода определить или даже вычислить адрес перехода, требуемый в данных условиях.
Ассемблер допускает различные формы описания косвенного перехода через ячейку сегмента данных:
jmp DS:go_addr
jmp word ptr go_addr
jmp go_addr
В первом варианте, использованном в приведенном выше фрагменте, указано, через какой сегментный регистр надлежит обращаться к ячейке go_addr, содержащей адрес перехода. Здесь допустима замена сегмента, если сегмент с ячейкой go_addr адресуется через другой сегментный регистр, например, ES или CS.
Во втором варианте подчеркивается, что переход осуществляется через ячейку размером в одно слово и, следовательно, является ближним. Ячейка go_addr могла быть объявлена с помощью директивы dd и содержать полный двухсловный адрес перехода, требуемый для реализации дальнего перехода. Однако ею можно воспользоваться и для ближнего перехода. Описатель word ptr перед именем ячейки с адресом перехода засташшет транслятор считать, что она имеет размер 1 слово (независимо от того, как она была объявлена), и что переход, следовательно, является ближним.
Наконец, возможен и самый простой, третий вариант, который совпадает по форме с прямым переходом, но, тем не менее, является косвенным, так как символическое обозначение go_addr является именем поля данных, а не программной меткой. В этом варианте предполагается, что сегмент, в котором находится ячейка go_addr, адресуется по умолчанию через регистр DS, хотя, как и во всех таких случаях, допустима замена сегмента. Тип перехода (ближний или дальний) определяется, исходя из размера ячейки go_addr. Однако этот вариант не всегда возможен. Для его правильной трансляции необходимо, чтобы транслятору к моменту обработки предложения
было уже известно, что собой представляет имя go_addr. Этого можно добиться двумя способами. Первый — расположить сегмент данных до сегмента команд, а не после, как в приведенном выше примере. Второй — заставить транслятор обрабатывать исходный текст программы не один раз, как он это делает по умолчанию, а несколько.
Число проходов для транслятора TASM можно задать при его вызове с помощью ключа /m#, где # — требуемое число проходов. В нашем случае достаточно двух проходов.
В приведенных примерах адрес поля памяти с адресом точки перехода задавался непосредственно в коде команды косвенного перехода. Однако этот адрес можно задать и в одном из регистров общего назначения (ВХ, SI или DI). Для приведенного выше примера косвенного перехода в точку go, адрес которой находится в ячейке go_addr в сегменте данных, перс-ход с использованием косвенной регистровой адресации может выглядеть следующим образом:
mov BX, offset go_addr ;В ВХ смещение поля с адресом перехода
jmp [BX] ;Переход в точку gо
Особенно большие возможности предоставляет методика косвенного перехода с использованием базово-индексной адресации через пары регистров, например, [BX][SI] или [BX][DI]. Этот способ удобен в тех случаях, когда имеется ряд альтернативных точек перехода, выбор которых зависит от некоторых условий. В этом случае в сегменте данных создается не одно поле с адресом, а таблица адресов переходов. В базовый регистр ВХ загружается адрес этой таблицы, а в один из индексных регистров — определенный тем или иным способом индекс в этой таблице. Переход осуществляется в точку, соответствующую заданному индексу. Структура программы, использующий такую методику, выглядит следующим образом:
code segment
mov BX, off set go_tbl ;Загружаем в ВХ базовый адрес таблицы
mov SI, 4 ;Вычисленное каким-то
;образом смещение в таблице
jmp [BX] [SI] ;Если индекс =4, переход в точку goЗ
…
gol: ;1-я точка перехода
…
gо2 : ;2-я точка перехода
…
gоЗ: ;3-я точка перехода
…
code endsdata segment
go_tbl label word ;Таблица адресов переходов
gol_addr dw gol ;Адрес первой альтернативной
;точки переходаgo2_addr dw go2 ;Адрес второй альтернативной
;точки переходаgo3_addr dw доЗ ;Адрес третьей альтернативной
;точки переходаdata ends
Приведенный пример носит условный характер; в реальной программе индекс, помещаемый в регистр SI, должен вычисляться по результатам анализа некоторых условий.
Наконец, существует еще одна разновидность косвенного перехода, в котором не используется сегмент данных, а адрес перехода помещается непосредственно в один из регистров общего назначения. Часто такой переход относят к категории прямых, а не косвенных, однако это вопрос не столько принципа, сколько терминологии.
Применительно к обозначениям последнего примера такой переход будет выглядеть, например, следующим образом:
Здесь, как и в предыдущих вариантах, имеется возможность вычисления адреса перехода, однако нельзя этот адрес индексировать.
Косвенный дальний (межсегментный) переход. Как и в случае ближнего перехода, переход осуществляется по адресу, который содержится в ячейке памяти, однако эта ячейка имеет размер 2 слова, и в ней содержится полный (сегмент плюс смещение) адрес точки перехода. Программа в этом случае должна включать по меньшей мере два сегмента команд. Структура программы с использованием косвенного дальнего перехода может выглядеть следующим образом:
codel segment
assume CS:codel,DS:data
…
jmp DS:go_addr ; Код FF 2E dddd
…
codel endscode2 segment
assume CS:code2
…
go: ;Точка перехода в другом сегменте команд
…
code2 endsdata segment
…
go_addrdd go ;Двухсловный адрес точки перехода
…
data ends
Точка перехода go находится в другом сегменте команд этой двухсегментной программы. В коде команды dddd обозначает относительный адрес слова go_addr в сегменте данных. Ячейка go_addr объявляется директивой dd (define double, определить двойное слово) и содержит двухсловный адрес точки перехода; в первом слове содержится смещение go в сегменте команд codel, во втором слове сегментный адрес codel. Оба компонента адреса перехода могут быть вычислены и помещены в ячейку go_addr по ходу выполнения программы.
Как и в случае ближнего косвенного перехода, ассемблер допускает различные формы описания дальнего косвенного перехода через ячейку сегмента данных:
jmp DS:go_addr ;Возможна замена сегмента
jmp dword ptr go_addr ;Если поле go_addr объявлено
;операторами dwjmp go_addr ;Характеристики ячейки должны
;быть известны
Для дальнего косвенного перехода, как и для ближнего, допустима адресация через регистр общего назначения, если в него поместить адрес поля с адресом перехода:
Возможно также использование базово-индексной адресации, если в сегменте данных имеется таблица с двухсловными адресами точек переходов.
Источник: www.i-assembler.ru