Как избежать переполнения стека с помощью батута

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

#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);
}

person Community    schedule 15.04.2020    source источник
comment
Где определение trampoline?   -  person UnholySheep    schedule 15.04.2020
comment
thunk1, кажется, вызывает thunk_f, который вызывает thunk1, который вызывает thunk_f, который ...   -  person Robert Harvey    schedule 15.04.2020
comment
Что ж, вы избежите переполнения стека, не сделав этого.   -  person Robert Harvey    schedule 15.04.2020
comment
Я не знаю никаких законных вариантов использования непрерывного цикла. Каждый полезный экземпляр цикла в конечном итоге завершается.   -  person Robert Harvey    schedule 15.04.2020
comment
@PaulHankin Техника хорошо отработана. Здесь его просто неправильно применяют.   -  person Konrad Rudolph    schedule 15.04.2020
comment
weblambdazero.blogspot.com/2010/07/using-trampolines- in-c.html   -  person Robert Harvey    schedule 15.04.2020
comment
Я просто хочу избежать создания стековых фреймов с использованием функции трамплина. Я не говорю, что незавершенные циклы полезны; Я просто не знаю другого способа визуально увидеть кадры стека.   -  person    schedule 15.04.2020
comment
Прочтите статью, которую я только что дал.   -  person Robert Harvey    schedule 15.04.2020
comment
@RobertHarvey Я смогу добиться желаемого поведения с помощью функции батута, которую я определил выше.   -  person    schedule 15.04.2020
comment
get the behaviour I want какого поведения вы хотите? Каким должен быть результат вашей программы? 5? 6? Ваша программа просто выполняет while(1) { a++; }, но в виде рекурсивных функций. Когда следует прекратить увеличение a?   -  person KamilCuk    schedule 15.04.2020
comment
Двух минут недостаточно, чтобы прочитать эту статью. Тебе действительно стоит это прочитать.   -  person Robert Harvey    schedule 15.04.2020


Ответы (1)


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

В результате происходит переполнение стека. Вы можете избежать переполнения стека (но не бесконечного цикла), вернув 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;
  }
}

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

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

Наконец, я, вероятно, должен отметить, что это недопустимый 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);
}
person Konrad Rudolph    schedule 15.04.2020
comment
И, конечно же, thunk_f может также выполнять передачу продолжения вместо явного вызова thunk1, заменяя тело на return (struct f) {thunk1};. - person Konrad Rudolph; 15.04.2020