Polinômios esparsos podem ser representados eficientemente por meio de
listas encadeadas. Neste exercício-programa você escreverá uma
biblioteca que usa listas encadeadas para implementar o tipo abstrato de
dados "polinômio". A interface da sua biblioteca deve ser o arquivo
polinomio.h
, cujo conteúdo é o seguinte:
#ifndef __POLINOMIO_H__ typedef struct termo *Polinomio; Polinomio cria_monomio(double coef, int exp); Polinomio soma(Polinomio p, Polinomio q); Polinomio subtrai(Polinomio p, Polinomio q); Polinomio multiplica(Polinomio p, Polinomio q); Polinomio divide(Polinomio p, Polinomio q); Polinomio resto(Polinomio p, Polinomio q); Polinomio oposto(Polinomio p); Polinomio deriva(Polinomio p); Polinomio copia(Polinomio p); int grau(Polinomio p); double calcula(Polinomio p, double x); void imprime(Polinomio p, FILE *arq); void libera(Polinomio p); #define __POLINOMIO_H__ #endif
Note que o arquivo polinomio.h
especifica o que se pode fazer com
um Polinomio
(isto é, as operações sobre polinômios), mas não
informa como um Polinomio
é implementado. O tipo
Polinomio
é definido como um apontador para uma estrutura cuja
definição não é parte da interface da biblioteca. A definição da estrutura
termo
não aparece no arquivo polinomio.h
. Isso faz
com que o tipo Polinomio
seja abstrato. Note, ainda, que esse
tipo também é de primeira classe, pois podemos ter múltiplos polinômios e
podemos armazenar esses polinômios em variáveis do tipo
Polinomio
.
Abaixo descrevemos o que deve fazer cada uma das funções da biblioteca:
Polinomio cria_monomio(double coef, int exp);
coef
e cujo expoente é exp
e devolve o monômio
criado. Caso o parâmetro coef
seja igual a zero, esta
função cria um polinômio nulo (um polinômio que não possui nenhum
termo com coeficiente não nulo) e devolve o polinômio nulo criado.Polinomio soma(Polinomio p, Polinomio q);
p
e q
, e devolve como
valor da função a soma de p
e q
.Polinomio subtrai(Polinomio p, Polinomio q);
p
e q
, e devolve como
valor da função a diferença entre p
e q
, ou
seja, p-q
.Polinomio multiplica(Polinomio p, Polinomio q);
p
e q
, e devolve como
valor da função o produto de p
e q
.Polinomio divide(Polinomio p, Polinomio q);
p
e q
, e devolve como
valor da função o quociente da divisão de p
por
q
. Veja o exemplo no item abaixo.Polinomio resto(Polinomio p, Polinomio q);
p
e q
, e devolve como
valor da função o resto da divisão de p
por
q
. Polinomio oposto(Polinomio p);
p
e devolve como valor da função o
oposto de p
. Polinomio deriva(Polinomio p);
p
e devolve como valor da função
a derivada de p
.Polinomio copia(Polinomio p);
p
e devolve como valor da função uma
cópia de p
.int grau(Polinomio p);
p
e devolve como valor da função o
grau de p
.double calcula(Polinomio p, double x);
p
e um real x
e devolve o
valor do polinômio p
em x
.void imprime(Polinomio p, FILE *arq);
p
e o imprime no arquivo
arq
. O parâmetro arq
deve ser um arquivo
aberto para escrita.void libera(Polinomio p);
p
.
Exceto pela função libera
,
nenhuma das funções acima modifica algum polinômio recebido como parâmetro.
Todos os polinômios devolvidos por essas funções são alocados dinâmicamente e
devem ser liberados (mediante chamadas à funcão libera
) quando
não tiverem mais utilidade para o cliente da biblioteca. Por exemplo: se uma
variável v
referenciar um Polinomio
, é um erro
deixar que v
saia de escopo sem antes executar
libera(v)
. Também é um erro atribuir outro Polinomio
a v
sem antes executar libera(v)
. Considere o
seguinte exemplo, que imprime a soma e o produto de dois polinômios dados:
Polinomio p, q, r;
p = ...; /* inicializa p com algum polinômio */
q = ...; /* inicializa q com outro polinômio */
v = soma(p, q); /* inicializa v com o polinômio soma p+q */
imprime(v, stdout); /* imprime o polinômio soma */
libera(v); /* libera o polinômio soma */
v = multiplica(p, q); /* atribui a v o polinômio produto p*q */
imprime(v, stdout); /* imprime o polinômio produto */
O fragmento de código acima estaria errado sem a chamada
libera(v)
. O fragmento a seguir também está errado, pois os
polinômios devolvidos pelas chamadas às funções soma
e
multiplica
são passados diretamente à função
imprime
, sem serem liberados:
Polinomio p, q, r; p = ...; /* inicializa p com algum polinômio */ q = ...; /* inicializa q com outro polinômio */ imprime(soma(p, q), stdout); /* imprime o polinômio soma p+q */ imprime(multiplica(p, q), stdout); /* imprime o polinômio produto p*q */
É importante entender que um cliente da biblioteca precisa chamar
funções da biblioteca para obter polinômios. O cliente não tem como "fabricar"
polinômios diretamente. As funções cria_monomio
,
soma
, subtrai
, multiplica
,
divide
, resto
, oposto
,
deriva
e copia
criam novos polinômios.
Cada polinômio deve ser armazenado em uma lista encadeada sem cabeça. Cada
célula dessa lista deve ser do tipo struct termo
e deve conter os
dados de um dos termos do polinômio: o coeficiente e o expoente, além do
apontador para o próximo termo. Apenas termos com coeficiente não-nulo devem
estar na lista e, para que a manipulação seja mais eficiente, os termos
devem aparecer na lista em ordem decrescente do expoente.
Exemplo 1: O polinômio p(x)=-x5+2x3+5x2-7 fica armazenado em uma lista de acordo com a seguinte figura.
inicio
|
|
|
|
|
Em cada uma das células acima, o campo prox
representa um
apontador para a célula seguinte na figura. O retângulo mais à esquerda é a
variável que contém o início (o endereço da primeira célula) da lista. Note a
ausência dos termos correspondentes a x4
e
x
, que neste polinômio têm coeficientes nulos.
Exemplo 2: O polinômio nulo p(x)=0 é representado pela lista que aparece na figura abaixo.
inicio
|
Note que essa lista é vazia, pois o polinômio p(x)=0 não possui nenhum
termo com coeficiente não nulo. Como a lista não possui nenhuma célula, a
variável que indica o início da lista contém NULL
.
A implementação da biblioteca (o arquivo polinomio.c
) deve
incluir as seguintes declarações:
#include <stdio.h> #include <stdlib.h> ... #include "polinomio.h" typedef struct termo { double coef; int exp; struct termo *prox; } Termo; static Termo *lista_livre = NULL;
A estrutura Termo
é usada para as células dos polinômios. Em
outras palavras, cada polinômio é uma lista encadeada de Termo
s.
Note, entretanto, que os programas que forem usar a sua biblioteca de
polinômios (os clientes da biblioteca) não precisam saber nada disso. Esses
programas não precisam conhecer a estrutura Termo
, nem saber que
internamente a biblioteca implementa polinômios como listas encadeadas sem
cabeça. Para escrever um cliente é suficiente conhecer a interface da
biblioteca (o arquivo polinomio.h
) e saber o que fazem as funções
dessa interface.
A lista livre
Sua implementação deve manter uma lista livre, com os Termo
s
alocados via malloc
mas disponíveis no momento. O apontador
lista_livre
aponta para o início dessa lista. Um novo
Termo
deve ser alocado via malloc
apenas quando a lista livre estiver vazia. A função cria_monomio
devolve um Polinomio
constituído por uma célula recém retirada
da lista livre ou recém alocada via malloc
. (Ela obtém essa
célula por meio de uma chamada à função aloca_termo
, descrita
abaixo.) A função libera
insere na lista livre todas as células
do polinômio referenciado por p
. (Ela faz isso mediante chamadas
à função libera_termo
, descrita abaixo.)
Não deixe sua implementação vazar memória, ou seja, inclua na lista livre todas as células que não estiverem em um polinômio no momento.
Sua biblioteca deve conter duas funções auxiliares com os seguintes protótipos:
static Termo *aloca_termo();
static void libera_termo(Termo *p);
Essas são funções de manipulação da lista livre. Todo acesso à lista livre é
feito por meio de chamadas a aloca_termo
ou a
libera_termo
. A função aloca_termo
devolve o
endereço de um Termo
retirado da lista livre ou (caso essa lista
esteja vazia) alocado diretamente via malloc
. A função
libera_termo
insere na lista livre o Termo
cujo
endereço é recebido como parâmetro. Opcionalmente você pode fazer com que
libera_termo
examine o comprimento da lista livre antes de
inserir um Termo
na lista. Se esse comprimento for menor que um
valor pré-fixado, a função insere o Termo
na lista livre. Caso
contrário libera_termo
faz uma chamada a free
para
devolver ao sistema a memória ocupada pelo Termo
. Esse esquema
evita que a lista livre cresça indefinidamente.
Note que as demais partes da biblioteca nunca precisarão chamar
malloc
ou free
para alocar ou liberar termos. A
motivação para se usar uma lista livre é precisamente a economia nas chamadas
a malloc
e free
, pois essas funções são mais
demoradas que as funções aloca_termo
e libera_termo
.
(Por que malloc
e free
não podem ser tão eficientes
quanto aloca_termo
e libera_termo
?)
Requisitos adicionais sobre a implementação da biblioteca
polinomio.c
. A compilação desse arquivo fonte produz o
arquivo objeto polinomio.o
, que deve ser incorporado (ligado)
a cada cliente da biblioteca. Um usuário da biblioteca (um programador
que escreve clientes) precisa apenas dos arquivos polinomio.h
(interface) e polinomio.o
(implementação já compilada).
Se você implementar a sua biblioteca corretamente e um colega seu
implementar um programa cliente corretamente, o cliente do seu colega
poderá usar o arquivo polinomio.o
que você gerou.
soma
e subtrai
deve ser eficiente, ou seja, cada um dos dois polinômios de
entrada deve ser percorrido apenas uma vez.
oposto
, deriva
,
copia
, calcula
, imprime
e
libera
devem ser deve ser eficientes, ou seja,
o polinômio de entrada deve ser percorrido apenas uma vez.
multiplica
, divide
e resto
devem ser tão eficientes quanto possível.
copia
deve fazer uma "cópia funda"
da lista encadeada, de modo que a lista original e a cópia não
compartilhem células. Não basta copiar só o endereço da célula inicial
(isso seria uma "cópia rasa"), nem copiar só a célula inicial de uma lista
que tenha mais de uma célula, nem copiar uma sequência de células que
forme um prefixo próprio da lista original.
static Polinomio multiplica_por_monomio(Polinomio p, double coef, int exp)
Essa função devolve o produto do polinômio p
pelo monomio cujo
coeficiente é coef
e cujo expoente é exp
. O
polinômio devolvido é alocado dinâmicamente e deve ser liberado (mediante
chamada à funcão libera
) quando não tiver mais utilidade para
quem chamou multiplica_por_monomio
.
Note que multiplica_por_monomio
é uma função interna da
biblioteca. Por esse motivo ela é declarada com static
e não
aparece no arquivo polinomio.h
.multiplica
, divide
e
resto
devem ser implementadas usando a função
multiplica_por_monomio
. (O cumprimento desse
requisito facilita bastante a tarefa de escrever aquelas
funções.)static
, de modo a serem
acessíveis apenas dentro da biblioteca.
Além da implementação das funções da biblioteca, você deverá entregar
um programa principal que use essas funções. O programa principal
é uma calculadora que mantém um conjunto de 23 variáveis identificadas pelas
letras minúsculas de 'a'
a 'w'
. Os valores dessas
variáveis são polinômios. O usuário da calculadora pode efetuar operações
envolvendo essas variáveis.
Este é um exemplo de uso da calculadora:
a?
a(x) = 0
p?
p(x) = 0
p: 3 4 5 -1 3 1 2
p(x) = 4x^5 - x^3 + x^2
q: 4 2 2 3 4 -1 0 1 3
q(x) = 3x^4 + x^3 + 2x^2 - 1
r=p+q
r(x) = 4x^5 + 3x^4 + 3x^2 - 1
s=p*q
s(x) = 12x^9 + 4x^8 + 5x^7 + 2x^6 - 5x^5 + 2x^4 + x^3 - x^2
t=s-r
t(x) = 12x^9 + 4x^8 + 5x^7 + 2x^6 - 9x^5 - x^4 + x^3 - 4x^2 + 1
u=r'
u(x) = 20x^4 + 12x^3 + 6x
u=~u
u(x) = -20x^4 - 12x^3 - 6x
v=t/p
v(x) = 3x^4 + x^3 + 2x^2 - 2
w=t%p
w(x) = -3x^4 - x^3 - 2x^2 + 1
a=~t
a(x) = -12x^9 - 4x^8 - 5x^7 - 2x^6 + 9x^5 + x^4 - x^3 + 4x^2 - 1
b=~t+p*v+w
b(x) = 0
f=p
f(x) = 4x^5 - x^3 + x^2
g=~(f+q)%(r'-r/q+v'*v)
g(x) = -4x^5 - 3x^4 - 3x^2 + 1
h=g+(f-(p+q)%(p-q))''*((p+q)'%~(p-q)')/(r+f)
h(x) = -4x^5 - 3x^4 - 3x^2 + 240x - 245
h(1.2345)
h(1.2345) = 28.2717
t?
t(x) = 12x^9 + 4x^8 + 5x^7 + 2x^6 - 9x^5 - x^4 + x^3 - 4x^2 + 1
t(1)
t(1) = 11
t(123.456)
t(123.456) = 8.0163e+19
p=p
p(x) = 4x^5 - x^3 + x^2
p=p*p
p(x) = 16x^10 - 8x^8 + 8x^7 + x^6 - 2x^5 + x^4
p(2)
p(2) = 15376
p=p*p
p(x) = 256x^20 - 256x^18 + 256x^17 + 96x^16 - 192x^15 + 80x^14 + 48x^13 - 47x^12 + 12x^11 + 6x^10 - 4x^9 + x^8
p(2)
p(2) = 2.36421e+08
As linhas em negrito pertencem ao arquivo de entrada (ou podem ter sido digitadas pelo usuário, de modo interativo). As demais linhas são geradas pela calculadora e formam a saída do programa.
Cada linha da entrada contém um comando para a calculadora. Todo comando
começa com uma letra minúscula de 'a'
a 'w'
. Essa
letra identifica uma variável da calculadora. O comando "v?
" (uma
varíavel da calculadora seguida do caractere "?
") é um comando
de consulta que imprime o polinômio associado à variável especificada. No
início da sua execução, a calculadora inicializa com polinômios nulos
todas as variáveis de 'a'
a 'w'
. (Seu programa
deve fazer isso!) Por esse motivo, a consulta "a?
" imprime a
informação "a(x) = 0
" (a
é um polinômio
nulo). A primeira das consultas "p?
" imprime informação análoga.
Um comando da forma "v: n <lista de n pares>
" atribui à
variável v
o polinômio cujos termos não nulos são especificados
por uma lista de n
pares de números. Em cada um desses pares de
números, o primeiro elemento é o coeficiente do termo (um número real) e o
segundo elemento é o expoente do termo (um número inteiro não
negativo). Assim, o comando
"p: 3 4 5 -1 3 1 2
" atribui à
variável p
um polinômio com três termos não nulos (o polinômio
4x5 - x3 + x2
).
Em resposta a esse comando, a calculadora imprime o polinômio atribuído à
variável p
(a informação
p(x) = 4x^5 - x^3 + x^2
). Note que
a lista de pares não precisa ser fornecida em ordem decrescente de expoente. No
comando
"q: 4 2 2 3 4 -1 0 1 3
"
os pares são fornecidos numa ordem arbitrária, mas os termos do polinômio
atribuído à variável q
aparecem em ordem decrescente de expoente
(q(x) = 3x^4 + x^3 + 2x^2 - 1
).
Um comando da forma "v=<expressão>
" atribui à variável
v
o polinômio resultante da avaliação da expressão, que deve ser
bem formada e pode conter os seguintes elementos:
a
" a "w
";+
" (soma), "-
"
(subtração), "*
" (multiplicação), "/
"
(quociente da divisão) e "%
" (resto da
divisão);~
" (oposto) e "'
"
(derivada).
Todos os operadores binários são infixos. O operador "~
" é
prefixo e o "'
" é posfixo. Todos os operadores podem ser
aplicados diretamente a variáveis ou a resultados de operações já efetuadas.
Em resposta a um comando da forma "v=<expressão>
", a
calculadora imprime o polinômio atribuído à variável v
. O exemplo
acima contém vários comandos desse tipo. Note que a mesma variável pode
aparecer nos dois lados da atribuição ("p=p
" e
"p=p*p
" são comandos válidos).
Finalmente, um comando da forma
"v(<um número real>)
" faz a
calculadora imprimir o valor do polinômio v
(mais precisamente, o
valor do polinômio associado à variável v
) sobre o número real
especificado. Em outras palavras, o programa calcula e imprime
v(x)
para o valor de x
especificado.
A precedência determina qual operador "precede" o outro, ou seja, qual deve ser efetuado primeiro. Esta é ordem de precedência dos operadores aceitos pela calculadora:
'
"~
"*
", "/
" e
"%
" (empatados, ou seja, com a mesma
precedência)+
" e "-
"
(empatados, ou seja, com a mesma precedência)Por exemplo, na expressão
a+b*c-d'*~e
o primeiro "*
" será efetuado antes do "+
", o
"'
" e o "~
" antes do segundo "*
", que
precederá o "-
".
A associatividade dos operadores binários é sempre da esquerda para a direita. Ou seja, na expressão
a+b-c+d
o primeiro "+
" é efetuado, depois o "-
" e finalmente
o segundo "+
".
Note que o segundo caractere de cada comando ('?'
,
':'
, '='
ou '('
) define o tipo do
comando. O primeiro caractere especifica a variável afetada pelo
comando. Você pode usar esses fatos para simplificar a implementação do
seu programa cliente.
Para implementar as variáveis da calculadora, utilize um vetor com 23 posições
(uma para cada letra minúscula de 'a'
a 'w'
), que
contenha em cada posição uma referência para um polinômio.
Mais especificamente, seu programa cliente deve manter um vetor
Polinomio var[23]; /* O tipo Polinomio é o definido pela biblioteca. */
de modo que
var[0]
seja o Polinomio
associado à variável cujo
identificador é a
,var[1]
seja o Polinomio
associado à variável cujo
identificador é b
,var[2]
seja o Polinomio
associado à variável cujo
identificador é c
, etc.
Em outras palavras, as 23 variáveis da calculadora (a
,
b
, c
, ..., w
) são implementadas pelas
23 posições do vetor var
(var[0]
, var[1]
,
var[2]
, ..., var[22]
). Dessa forma pode-se ter
acesso a um polinômio de modo bem eficiente, a partir da letra minúscula que
identifica o polinômio.
No início da execução do programa, todas as posições do vetor var
devem ser inicializadas com polinômios nulos. Para obter polinômios nulos,
faça chamadas à função cria_monomio
passando o valor zero no
parâmetro coef
.
De longe a maneira mais fácil para se avaliar uma expressão arbitrária contendo operadores infixos e subexpressões delimitadas por parênteses consiste destas duas etapas:
Sua calculadora deve usar esse método. A conversão da expressão original para a forma posfixa pode ser feita de modo similar ao mostrado nas notas de aula sobre pilhas. A avaliação da expressão posfixa deve ser feita da maneira natural, usando de uma pilha de operandos e resultados de operações.
Tome o seguinte cuidado com a leitura dos dados no seu programa cliente: leia
cada linha da entrada até o último caractere da linha (o caractere
'\n'
que termina toda linha), incluindo esse caractere. Se você
não fizer isso, quando você estiver querendo ler o primeiro caractere da
próxima linha você acabará pegando o '\n'
da linha anterior (ou
até mesmo algum caractere que apareceu antes desse '\n'
, como por
exemplo um espaço presente ao final da linha anterior). Para não ter esse tipo
de problema, faça seu cliente ler toda linha até o fim, passando para a linha
seguinte só depois de ler um '\n'
!
Este esqueleto de programa cliente
inclui uma função auxiliar (descarta_resto_da_linha
) que lê e
descarta todos os caracteres ainda não lidos da linha atual. Ele contém também
um fragmento da função main
com o laço de leitura da
entrada. Esse fragmento mostra uma chamada a
descarta_resto_da_linha
, efetuada quando o cliente encontra uma
linha cujo primeiro caractere não é uma variável válida. Faça o seu programa
cliente chamar descarta_resto_da_linha
sempre que a leitura dos
caracteres de um comando não chegar ao '\n'
que termina a linha:
depois de ler o último caractere útil do comando, descarte o resto da linha da
entrada.
Note que o nosso esqueleto de programa
cliente tem mais uma função auxiliar (le_polinomio
), esta
ainda não implementada. Implemente a função le_polinomio
e use-a
no tratamento do comando ":
"!
Além de conter a função principal (main
) da calculadora, o arquivo
cliente.c
deve ter pelo menos as seguintes funções:
static Polinomio le_polinomio(FILE *entrada);
static void converte(char original[], char posfixa[]);
original
uma expressão no formato aceito
pela calculadora e devolve na string posfixa
a expressão
posfixa correspondente.static Polinomio avalia(char posfixa[], Polinomio polinomio[]);
posfixa
uma expressão na forma posfixa
e o no parâmetro var
o vetor usado
para implementar as variáveis da calculadora. Devolve o
Polinomio
que é o resultado da avaliação da expressão
posfixa
.Seu programa cliente deve tratar os seguintes argumentos na linha de comando:
./cliente [-d] [arq-entrada] [arq-saida]A linha de comando acima pressupõe que o nome do arquivo executável é
cliente
.
Os argumentos aceitos pelo programa têm estes significados:
-d
("debug mode") especifica que a
calculadora deve imprimir informações adicionais no arquivo de
saída. Para cada comando de atribuição, devem ser impressas a expressão
original e a expressão posfixa correspondente.arq-entrada
é o nome do arquivo de
entrada.arq-saida
é o nome do arquivo de saída.Exemplos:
./cliente entrada.txt saida.txt
./cliente -d entrada.txt saida.txt
./cliente entrada.txt
./cliente -d entrada.txt
./cliente
./cliente -d
O tratamento dado a cada argumento é determinado pela posição do argumento
na linha de comando. O primeiro argumento diferente de "-d
" é o
nome do arquivo de entrada. O argumento seguinte é o nome do arquivo de
saída. Pode ocorrer omissão de argumentos:
Neste diretório você encontra os arquivos
polinomio.h
e esqueleto-cliente.c
, um
exemplo pré-compilado de biblioteca (arquivo polinomio.o
) e um exemplo
executável de programa cliente (arquivo cliente
), bem como o arquivo de entrada com os exemplos de uso
da calculadora apresentados nesta página. O diretório contém também
arquivos mallocc.h
e
mallocc.o
, pois a biblioteca
polinomio.o
usa a função
mallocc
.
Seu programa cliente deve funcionar com o arquivo polinomio.o
fornecido. Use-o para
testar seu cliente. Note que você pode escolher se começa implementando a
biblioteca ou se começa pelo cliente. Caso você opte por começar pelo cliente,
o arquivo polinomio.o
permitirá
que você rode o cliente mesmo antes da sua biblioteca estar pronta.
gcc
ou o
Code::Blocks
(que na verdade chama o gcc
para
fazer a compilação). Caso você use diretamente o gcc
, passe
ao compilador (na linha de comando) as seguintes opções:
-Wall -ansi -pedantic -O2 -U_FORTIFY_SOURCECaso você use o
Code::Blocks
, entre em
Settings -> Compiler and debugger... -> Compiler settings -> Compiler Flags,
selecione as quatro opcões correspondentes a -Wall
,
-ansi
, -pedantic
e -O2
, e clique
em OK. Entre também em
Settings -> Compiler and debugger... -> Compiler settings -> Other options,
digite -U_FORTIFY_SOURCE
na caixa de texto
Other options e clique OK.
ep2-<seu-nome>.tar.gz
ou
ep2-<seu-nome>.zip
.
Por exemplo: ep2-fulano.zip
..tar.gz
ou .zip
. (Exemplo:
ep2-fulano
.) Todos os arquivos desempacotados devem
estar dentro desse diretório.polinomio.h
com a interface da sua
biblioteca. O conteúdo desse arquivo deve ser o especificado
neste enunciado.polinomio.c
com a implementação da sua
biblioteca.cliente.c
com o programa cliente que
faz chamadas às funções da sua biblioteca. (É nesse arquivo que
está a função main
.).h
ou .c
que você utilizar para funções auxiliares compiladas
separadamente. Por exemplo, se você tiver um módulo compilado
separadamente contendo apenas a função mallocc
,
então você deverá entregar os arquivos
mallocc.h
e mallocc.c
. (Considere
a possibilidade de usar uma implementação de pilha compilada
separadamente.)