【问题标题】:Returning directly to main from a function call of a function call从函数调用的函数调用直接返回main
【发布时间】:2017-05-17 04:28:53
【问题描述】:

作为免责声明,这个问题与我的特定任务有关,我不要求任何人为我做作业。

所以基本上我应该实现一种从函数调用的函数调用直接返回到 main 的方法。

部分规定是我们不能使用汇编语言指令、gcc 的 asm() 或 gcc 内置函数。在 google 上对此进行了大量研究之后,我真的找不到任何可以查看的示例,甚至找不到 setjmp/longjmp 的源代码(此作业的目的是复制这些功能)。我已经向一些年纪较大的 CS 学生寻求建议,大多数人都无能为力,或者告诉我,他们很确定按照给定的规定这是不可能的。任何建议或指示(哈哈)将不胜感激。即使是朝着正确的方向轻推或确认作业没有我认为的那么复杂,也将不胜感激!

到目前为止我最好的尝试:

-使用 setjmp 函数来存储我们在 main 中中断的地址(例如 x = foo1(); 并将 x 传递给 setjmp(x)),然后让 foo2 调用我的 longjmp 函数,在 longjmp 中我'会让函数设置一个指针 (*p) 指向我的参数,然后 (*p-1) = x in main.的地址。

这不起作用,但我认为尝试更改调用堆栈中的返回地址是正确的想法,因为如果我理解正确,函数的参数直接位于堆栈中的返回地址之上。

这是我写的代码:

int setjmp(int v);
int longjmp(int v);
int fun1(void); 
int fun2(void); 
int *add; //using global, not sure if best idea
int main(void)
{
    int x = setjmp(x);
    foo1();
    return 0;
}
int setjmp(int v)
{
  add = &v; //used a global variable
  return 0;
}
int longjmp(int v)
{
  int *p; //pointer
  p = &v; //save argument address
  *(p-1) = *add; //return address = address in main
  return 1;
}
int foo1(void)
{
    printf("hi1");
    foo2();
    printf("hi2");
    return 0;
}
int foo2(void)
{
    int a;      
    longjmp(a);
    return 0;
}//output SHOULD be "hi1"
 //output is currently "hi1" "hi2"

因为我没有评论的每一行都是一个骨架,我无法改变它。

如果有什么问题,请提前原谅,我对 C 很陌生。谢谢。

【问题讨论】:

  • 您可能想考虑如何让堆栈指针具有您希望它具有的值,而不是专注于返回地址。
  • 接上一条评论,你可能想看看Function Prologue and Epilogue in C
  • 这在标准 C 中是不可能的。C 对调用堆栈的表示一无所知。对于 x86 上的标准 C 调用,没有汇编程序是不可能的,因为您需要操作 esp/ebp 而 C 没有寄存器的概念。在某些不同的体系结构或堆栈指针位于内存位置的某些不同调用约定上,这可能是可能的。当然,这根本不是便携的。
  • 为什么需要这个?
  • 如果你需要这样做,(例如,在一堆函数调用中引起错误或其他“异常”情况的通知),你使用了错误的语言。使用支持异常的语言,例如。 C++

标签: c


【解决方案1】:

“所以基本上我应该实现一种从函数调用的函数调用中直接返回main的方法。”

这个要求是无稽之谈。任何满足该要求的尝试都将导致垃圾代码。训练写这样的东西是直接有害的做法。这是一个非常糟糕的作业,你的老师应该为在没有免责声明的情况下教你糟糕的练习而感到羞耻。在现实世界的程序中也没有理由做这样的事情。

您不能在纯 C 中实现 setjmp/longjmp 函数,您必须使用内联汇编程序。它们保存程序计数器和特定系统所需的其他类似东西。他们还干预堆栈指针,这是他们危险的原因之一。

因此,在标准 C 中执行此操作的唯一方法是使用来自 setjmp.h 的标准库函数 setjmp/longjmp。这些被广泛认为是非常糟糕和危险的,因为它们会导致无法阅读的意大利面条式编程和许多形式的未定义行为。 C 标准中未定义行为的一个示例:

在longjmp之后,尝试访问自动对象的值 不具有 volatile 限定类型的存储持续时间,函数本地 包含相应 setjmp 宏的调用,该宏已更改 在 setjmp 调用和 longjmp 调用之间

永远不要使用这些函数。


话虽如此,这就是你编写可怕而危险的程序的方式:

// BAD! NEVER WRITE SPAGHETTI CODE LIKE THIS!

#include <stdio.h>
#include <setjmp.h>
#include <stdbool.h>

static jmp_buf jmp_main;

void func2 (bool one_more_time)
{
  puts(__func__);
  if(one_more_time)
  {
    longjmp(jmp_main, !one_more_time);
  }
  printf("end of "); puts(__func__);
}

void func1 (bool one_more_time)
{
  puts(__func__);
  func2(one_more_time);
  printf("end of "); puts(__func__);
}

int main (void)
{
  bool one_more_time = (bool)!setjmp(jmp_main);
  puts(__func__);  
  func1(one_more_time);
  printf("end of "); puts(__func__);
}

输出:

main
func1
func2
main
func1
func2
end of func2
end of func1
end of main

【讨论】:

    【解决方案2】:

    我认为问题没有那么复杂。您应该使用返回值的条件调用另一个函数,这样如果它是某个值(比如 0),则返回,否则继续该函数。 因此,您可以将foo2(); 替换为:

    if (foo2() == 0) return 0;
    

    现在在foo2() 函数return 0; 中,只要您希望返回foo1() 的主函数调用,否则继续使用foo1() 语句。此处无需使用 setjmp。

    这篇文章可能对您也有帮助:Tread programming examples

    【讨论】:

    • 欢迎来到 StackOverflow 并感谢您的帮助。据我了解您的建议,它是关于将调用周围的代码更改为foo2();。据我了解,该代码是不可编辑骨架的一部分,OP 在问题中对其进行了描述。因此,我认为您的建议并不能解决上述问题。你能详细说明一下吗?
    猜你喜欢
    • 1970-01-01
    • 2012-06-05
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    • 2019-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多