Подтвердить что ты не робот

Как "перейти" в другую функцию в c?

В основном я пытаюсь имитировать код сборки в C.

Вот код C:

int main()
{
   test();
main_next:
   printf("Hello, World!");
}

void test()
{
     goto main_next;
}

Пытаясь скомпилировать этот код (Linux 32 бит, gcc 4.6.3), я получил эту ошибку:

 error: label ‘main_randomtag_next’ used but not defined

Кто-нибудь знает, как сделать такой межпроцессный переход в C?

Спасибо!

4b9b3361

Ответ 1

Но как насчет children? stack?

goto между функциями не имеет никакого смысла, если вы думаете о стеке. Что будет в стеке, когда вы прыгаете? Функции источника и назначения могут потенциально иметь разные аргументы и другое возвращаемое значение. К кому будет возвращаться новая функция? Будет ли его возвращаемое значение даже иметь смысл для вызывающего? Вызывающий вызывал функцию источника, а не функцию назначения.

Возврат к вызывающему абоненту?

Рассмотрим ваш пример:

int main()
{
   test();
main_next:
   printf("hello, world);
}

void test()
{
     goto main_next;
}

Что происходит, когда выполняется goto? Я предполагаю, что вы захотите, чтобы это вернуло стек обратно к вызывающей функции main(). goto фактически будет таким же, как return, изменив стек вызовов из:

main()                            main()
|                   to            
+--> test()                       

Но что, если вы хотите перейти к функции, которая не находится в стеке вызовов? Что тогда?

Или заменить текущую функцию?

Другая интерпретация заключается в том, что goto заменит существующий вызов test() на один на main(). Стек вызова изменится с:

main()                            main()
|                   to            |
+--> test()                       +--> main()

Теперь main() рекурсивно вызывает себя, а нижний main() вернется к верхнему main() — кто, кстати, ожидает возвращаемое значение void, но получит int.

setjmp и longjmp

Ближе всего вы можете получить setjmp/longjmp. Они позволяют сохранять и восстанавливать контекст стека для нелокальной пересылки, позволяя вам переходить между вызовами функций.

setjmp и longjmp обойти проблемы, описанные в (a) сохранении и восстановлении полного контекста стека при прыжке, и (b) не разрешать переходы, если контекст стека уже недействителен. Я цитирую из справочную страницу (внимание мое):

setjmp() и longjmp (3) полезны для устранения ошибок и прерываний, встречающихся в низкоуровневой подпрограмме программы. setjmp() сохраняет контекст/среду стека в env для последующего использования longjmp (3). Контекст стека будет недействительным, если функция, называемая setjmp(), вернется.

Другими словами, longjmp является в основном эквивалентом C , бросающим исключение. Функция низкого уровня может развернуть стек вызовов и возобновить выполнение с гораздо более высокой функцией.

Это также ужасно сложно использовать, и редко хорошая идея. Снова, со страницы man:

setjmp() и sigsetjmp() делают программы трудными для понимания и поддержки. Если возможно, следует использовать альтернативу.

Ответ 2

GCC сначала создает файл сборки, а затем собирает его, так что как создать ярлыки с помощью встроенной сборки?

void test()
{
    __asm__ volatile ( 
         "jmp main_next"
    );
}


int main()
{
    test();
    __asm__ volatile ( 
        "main_next:"
    );
    printf("hello, world");
}

Однако это (очевидно) не должно использоваться в реальных случаях, так как оно вообще не заботится о стеке.

Ответ 3

ну, не могу сказать лучше, чем мудрость http://c-faq.com/style/stylewars.html!

В принципе, если вы хотите эмулировать поведение ASM, используя только C, тогда вы должны использовать все возможности ветвления C/С++. И использование функций и стека функций на самом деле является улучшением по сравнению с gotos и тегами. Это то, что структурированное программирование связано с тем, как @ssg мудро сказал!

Ответ 4

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

Итак, в этом элементарном примере вам нужно просто написать вместо возврата mainonext.

Но в других случаях это немного сложнее, потому что вы должны понимать, что хотите.

Вам нужно иметь код после main_next: как если бы он был написан в test()? Вы должны напомнить, что локальные переменные кадры этих двух функций различны. Это означает, что если вы просто сделаете прыжок, вы будете использовать имена переменных, используемых в основном, но вы будете ссылаться на фрейм стека, созданный тестом(). Это означает, что если два кадра несовместимы, могут произойти очень странные вещи.

Проблема в том, что вы хотите точно и почему?

Если вы думаете только о сборке, и вы не используете переменные в кадрах стека, все в порядке. Но что вы собираетесь делать без переменных?

Есть способы сделать то, что вы хотите, но вы должны решить, что именно вам нужно, и я могу сказать вам, как это можно сделать!