Functional programming 跳转和呼叫的区别

Functional programming 跳转和呼叫的区别,functional-programming,recursion,computer-science,Functional Programming,Recursion,Computer Science,跳转指令和呼叫指令有何不同?它与更高级别的概念(如GOTO或过程调用)有何关系?(我的比较正确吗?) 我是这样想的: 跳转或转到是将控件转移到另一个位置,并且控件不会自动返回到调用它的点 另一方面,调用或过程/函数调用返回到调用它的点。由于本质上的这种差异,语言通常使用堆栈,并推送堆栈框架“记住”每个调用的过程返回的位置。这种行为也适用于递归过程。在尾部递归的情况下,不需要为每个调用“推送”堆栈帧 您的回答和评论将不胜感激。如果您正在讨论x86汇编中的CALL/JMP或类似的东西,您基本上是对的

跳转指令和呼叫指令有何不同?它与更高级别的概念(如GOTO或过程调用)有何关系?(我的比较正确吗?)

我是这样想的:

跳转或转到是将控件转移到另一个位置,并且控件不会自动返回到调用它的点

另一方面,调用或过程/函数调用返回到调用它的点。由于本质上的这种差异,语言通常使用堆栈,并推送堆栈框架“记住”每个调用的过程返回的位置。这种行为也适用于递归过程。在尾部递归的情况下,不需要为每个调用“推送”堆栈帧


您的回答和评论将不胜感激。

如果您正在讨论x86汇编中的CALL/JMP或类似的东西,您基本上是对的。主要区别在于:

  • JMP执行到某个位置的跳转,而不做任何其他操作
  • 调用将当前指令指针推送到堆栈上(而不是在当前指令之后),然后将JMPs推送到该位置。通过RET,你可以回到原来的位置
通常,调用只是使用JMP实现的一个方便函数。你可以这样做

          movl $afterJmp, -(%esp)
          jmp location
afterJmp:

而不是打电话。

我想你已经大致了解了

这取决于体系结构,但通常在硬件级别:

  • 一条跳转指令将改变程序,以在程序的不同部分继续执行

  • call指令将当前程序位置(或当前位置+1)推送到,并跳转到程序的另一部分。然后,返回指令将从调用堆栈中弹出该位置,并跳回原始位置(或原始位置+1)

因此,跳转指令接近于
GOTO
,而调用指令接近于过程/函数调用

此外,由于在进行函数调用时使用调用堆栈,通过递归将过多的返回地址推送到调用堆栈将导致错误


在学习汇编时,我发现处理处理器比处理x86处理器更容易,因为它的指令更少,操作更简单。

关于跳转和调用之间的区别,你说得完全正确

在带有尾部递归的单个函数的示例中,编译器可能能够重用现有堆栈框架。但是,使用相互递归的函数可能会变得更复杂:

void ping() { printf("ping\n"); pong(); }
void pong() { printf("pong\n"); ping(); }

考虑以下情况:ping()和pong()是更复杂的函数,具有不同数量的参数。详细讨论了GCC的尾部递归实现。

根据微处理器,首先检查条件,然后执行跳转操作(转到其他代码),并且不返回。
调用操作就像是在
c
语言中调用函数,当函数被执行时,它会返回来完成它的执行。

对您的想法有一个更正:我们不需要堆栈框架,因此可以简单地在那里使用JMP,这不仅是尾部递归,而且通常是尾部调用(只要参数设置正确)。

当您说:“调用只是使用JMP实现的一个方便函数。”这是否意味着调用不是原子的(一条处理器指令)?你的意思是,就像一个线程可以在movl之间中断一样,但在jmp本身之前,如果一个调用将作为两条指令来实现?查看文档:,它说该过程确实首先移动堆栈上的地址,然后跳转,清楚地将其表示为一个序列。我不明白为什么它应该是原子的,另外,这又有什么关系?这很简单这与jmp之后您会被中断的情况相同,因为jmp是无条件的。如果
CALL
记住了您跳转的位置,这意味着太多
CALL
s可能会溢出堆栈(如果您不
RET
urn),而使用
JMP
则不可能损坏堆栈,但另一方面,除非存储位置,否则无法跳回从您自己跳转的位置。是否正确?在本例中,C的
goto
应等于
JMP
。由于
CALL
不执行任何条件检查,因此用于比较的唯一有效跳转指令是
JMP
,它无条件跳转。