Não é possível implementar semântica de chamada de função sem usar algum tipo de pilha. Só é possível jogar jogos de palavras (por exemplo, use um nome diferente, como "FILO return buffer").
É possível usar algo que não implementa semântica de chamada de função (por exemplo, estilo de passagem de continuação, atores) e depois construir semântica de chamada de função; mas isso significa adicionar algum tipo de estrutura de dados para rastrear onde o controle é passado quando a função retorna e essa estrutura de dados seria um tipo de pilha (ou uma pilha com um nome / descrição diferente).
Imagine que você tem muitas funções que podem se chamar. No tempo de execução, cada função deve saber para onde retornar quando a função sair. Se você first
ligar second
, você tem:
second returns to somewhere in first
Então, se você tiver second
chamadas third
:
third returns to somewhere in second
second returns to somewhere in first
Então, se você tiver third
chamadas fourth
:
fourth returns to somewhere in third
third returns to somewhere in second
second returns to somewhere in first
Como cada função é chamada, mais informações "para onde retornar" devem ser armazenadas em algum lugar.
Se uma função retornar, suas informações "para onde retornar" serão usadas e não serão mais necessárias. Por exemplo, se fourth
retornar para algum lugar third
, a quantidade de informações "para onde retornar" se tornaria:
third returns to somewhere in second
second returns to somewhere in first
Basicamente; "semântica de chamada de função" implica que:
- você deve ter informações "para onde retornar"
- a quantidade de informações cresce à medida que as funções são chamadas e é reduzida quando as funções retornam
- a primeira parte de "onde retornar" as informações armazenadas será a última parte de "onde retornar" as informações descartadas
Isso descreve um buffer FILO / LIFO ou uma pilha.
Se você tentar usar um tipo de árvore, todos os nós da árvore nunca terão mais de um filho. Nota: um nó com vários filhos só pode acontecer se uma função chamar 2 ou mais funções ao mesmo tempo , o que exige algum tipo de simultaneidade (por exemplo, threads, fork (), etc) e não seria "semântica de chamada de função". Se todos os nós da árvore nunca tiverem mais de um filho; então essa "árvore" seria usada apenas como um buffer FILO / LIFO ou uma pilha; e como é usado apenas como um buffer ou pilha FILO / LIFO, é justo afirmar que a "árvore" é uma pilha (e a única diferença são jogos de palavras e / ou detalhes de implementação).
O mesmo se aplica a qualquer outra estrutura de dados que possa ser usada para implementar a "semântica de chamada de função" - ela será usada como uma pilha (e a única diferença são jogos de palavras e / ou detalhes de implementação); a menos que quebre "semântica de chamada de função". Nota: Eu forneceria exemplos para outras estruturas de dados, se pudesse, mas não consigo pensar em nenhuma outra estrutura que seja um pouco plausível.
Obviamente, como uma pilha é implementada é um detalhe da implementação. Pode ser uma área da memória (onde você monitora um "topo da pilha atual"), pode ser algum tipo de lista vinculada (onde você monitora a "entrada atual na lista") ou pode ser implementada em alguns outro jeito. Também não importa se o hardware possui suporte interno ou não.
Nota: Se apenas uma chamada de qualquer procedimento puder estar ativa a qualquer momento; então você pode alocar estaticamente espaço para informações "para onde retornar". Ainda é uma pilha (por exemplo, uma lista vinculada de entradas alocadas estaticamente usadas de maneira FILO / LIFO).
Observe também que existem algumas coisas que não seguem a "semântica de chamada de função". Essas coisas incluem "semânticas potencialmente muito diferentes" (por exemplo, passagem de continuação, modelo de ator); e também inclui extensões comuns para "semântica de chamada de função", como simultaneidade (threads, fibras, o que for), setjmp
/ longjmp
, manipulação de exceção etc.