如何“转到”c中的不同功能?

时间:2014-01-25 19:21:39

标签: c assembly calling-convention

基本上我试图在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中进行这种程序间转换?

谢谢!

4 个答案:

答案 0 :(得分:33)

但是孩子怎么办?堆叠?

如果考虑堆栈,函数之间的

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。这些允许您保存和恢复非本地goto的堆栈上下文,允许您在函数调用之间跳转。

setjmplongjmp解决了我所描述的问题:(a)跳转时保存和恢复完整的堆栈上下文,以及(b)如果堆栈上下文不再有效则不允许跳转。我引用the man page(强调我的):

  

setjmp()和longjmp(3)对于处理程序的低级子例程中遇到的错误和中断很有用。 setjmp()将堆栈上下文/环境保存在env中,供以后使用longjmp(3)。 如果调用setjmp()的函数返回,则堆栈上下文将无效。

换句话说,longjmp基本上是抛出异常的C等价物。低级函数可以展开调用堆栈并在更高级别的函数中恢复执行。

使用它也非常棘手,很少有好主意。再次,从手册页:

  

setjmp()和sigsetjmp()使程序难以理解和维护。如果可能,应该使用替代方案。

答案 1 :(得分:5)

GCC首先生成汇编文件,然后才汇编它,那么使用内联汇编创建标签呢?

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


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

然而,这(显然)不应该在实际情况下使用,因为它根本不会处理堆栈。

答案 2 :(得分:1)

好吧,不能说明http://c-faq.com/style/stylewars.html的智慧!

基本上,如果您只想使用C模拟ASM的行为,那么您应该实际使用C / C ++的所有分支功能。使用函数和函数堆栈实际上是对gotos和tags的改进。正如@ssg明智地说的那样,这就是结构化编程的全部内容!

答案 3 :(得分:1)

不允许从另一个内部的函数跳转。 问题是函数“test”在堆栈上有一个返回地址,可能是变量的框架。 所以为了做到这一点,你应该清理可选框架并使用main_next的地址更改堆栈上的地址:

所以在这个基本的例子中,你应该写而不是goto main_next return。

但在其他情况下,它有点复杂,因为你必须了解你想要的东西。

你需要在main_next之后得到代码:好像它是用test()编写的吗? 您应该提醒这两个函数的局部变量帧是不同的。 这意味着如果你只是跳转,那么你将使用main中使用的变量名称,但是你将引用test()创建的堆栈帧。 这意味着如果两个帧不兼容,那么可能会发生非常奇怪的事情。

问题是你想要什么,为什么?

如果你考虑只是汇编,并且你不在堆栈帧中使用变量,那就没问题。 但是如果没有变量你会怎么做?

有办法做你想做的事,但你应该确定你需要什么,我可以告诉你如何做到这一点!