Как избежать переполнения стека с помощью батута - PullRequest
0 голосов
/ 15 апреля 2020

Функция батута в программе ниже работает правильно. Я думаю, что приведенная ниже программа приводит к переполнению стека, потому что функции thunk_f и thunk1 бесконечно вызывают друг друга, что приводит к созданию новых кадров стека. Тем не менее, я хочу написать программу, которая будет вести себя аналогично неопределенному l oop, так как батуты должны предотвращать переполнение стека.

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

void trampoline(void *(*func)()) {
  while (func) {
    void *call = func();
    func = (void *(*)())call;
  }
}

void *thunk1(int *param);
void *thunk_f(int *param);

void *thunk1(int *param)
{
  ++*param;
  trampoline(thunk_f(param));
  return NULL;
}

void *thunk_f(int *param) 
{
  return thunk1(param);
}

int main(int argc, char **argv)
{
  int a = 4;
  trampoline(thunk1(&a));
  printf("%d\n", a);
}

1 Ответ

3 голосов
/ 15 апреля 2020

Вы используете батут неправильно: вместо того, чтобы позволить ему вызывать вашу функцию thunk_f, вы вызываете ее с результатом функции thunk_f.

В результате, вы получаете переполнение стека. Вы можете избежать переполнения стека (но не бесконечного l oop), возвращая thunk_f вместо его вызова:

void *thunk1(int *param)
{
  ++*param;
  return thunk_f;
}

И вызывая trampoline в main правильно:

int main(int argc, char **argv)
{
  int a = 4;
  trampoline(thunk1, &a);
  printf("%d\n", a);
}

И, конечно, для этого необходимо, чтобы trampoline получил дополнительный аргумент, чтобы передать параметр &a в:

void trampoline(void *(*func)(int *), int *arg) {
  while (func) {
    void *call = func(arg);
    func = (void *(*)())call;
  }
}

Это работает - но, как уже было отмечено, это просто бесконечный l oop без вывода. Чтобы увидеть, что происходит, поместите printf внутри thunk1:

void *thunk1(int *param)
{
  printf("%d\n", ++*param);
  return thunk_f;
}

Наконец, я, вероятно, должен отметить, что это неверно C, потому что преобразование между указателем объекта и функцией недопустимо указатель (всегда компилировать с предупреждениями pedanti c!). Чтобы сделать код легальным, оберните указатель функции в объект:

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

struct f {
    struct f (*p)(void *);
};

void trampoline(struct f f, void *args) {
    while (f.p) {
        f = (f.p)(args);
    }
}

struct f thunk1(void *param);
struct f thunk_f(void *param);

struct f thunk1(void *param) {
    printf("%d\n", ++*((int *) param));
    return (struct f) {thunk_f};
}

struct f thunk_f(void *param) {
    return thunk1(param);
}

int main() {
    int a = 4;
    trampoline((struct f) {thunk1}, &a);
}
...