Prazo de entrega:
Segunda chance:
Esta tarefa tem peso 2.
Você irá implementar o módulo de memória do Batman! Para isso, precisará implementar e utilizar vetor dinâmico.
A Lei de Moore deixou de ser uma verdade há alguns anos. Durante um tempo, a indústria de processadores se apegou ao Escalonamento de Dennard para aumentar o desempenho de cada core, aumentando a velocidade de clock. Em paralelo, incentivados pela regra de Amdahl’s, a quantidade de cores em um chip cresceu consideravelmente na última década (o primeiro processador comercial com mais de um core foi lançado em 2005). Mais recentemente, a tendência parece levar às Arquiteturas Específicas de Domínio para extrair ainda mais desempenho. O exemplo mais notável são as placas aceleradoras de aprendizado de máquina lançadas pela Google. Mas isso é spoiler de uma outra disciplina.
Neste contexto, o Homem-Morcego, Cavaleiro das Trevas, Cruzado Encapuzado, Maior Detetive do Mundo, primeiro de seu nome, mas também conhecido como Batman, precisa de sua ajuda para construir um novo bat-computador para combater o crime em Gothan City. Você, como grande fã do trabalho do vigilante noturno, se colocou à disposição para implementar as funções de gerenciamento da bat-memória.
Implemente um programa batcomputador
que simula algumas operações da
bat-memória. Para isso, você precisará de um vetor dinâmico de inteiros para
representar os dados armazenados na memória. Tome cuidado com as restrições do
nosso herói, às vezes ele é meio exigente.
Operações
Em todos os testes, a bat-memória irá começar com 8 inteiros e crescer de acordo com a necessidade. A primeira linha da entrada contém um inteiro, que representa o número de operações a serem analisadas. Cada uma das linhas seguintes corresponde a uma operação de memória.
Bat-alloc
A operação de bat-alloc
guarda uma lista de inteiros na bat-memória virtual do
seu bat-computador e imprime a posição de início. Batman pediu que todo vetor de
inteiros seja armazenado de forma contínua, ou seja, um vetor de 4 inteiros não
pode ter seus inteiros colocados espalhados pela bat-memória.
Além disso, Batman pediu para você sempre colocar o vetor no primeiro espaço de memória disponível em que ele cabe, seguindo a ordem dos índices. Assim, se um vetor couber começando na posição 5 ou na posição 40, você sempre irá escolher a posição 5.
Operações como esta serão lidas na forma:
bat-alloc N LISTA
Essa operação armazena uma lista de N inteiros na memória virtual. A linha seguinte contém um exemplo de operação para armazenar 5 inteiros na memória.
bat-alloc 5 10 12 16 18 42
Após realizada a operação, você deve imprimir o local da memória onde o vetor foi armazenado. Há um detalhe importante: a primeira posição do espaço armazenado deve conter o tamanho do vetor. Assim, na bat-memória, teríamos algo como na figura:
Para este exemplo, a saída seria:
31
Caso não haja nenhum espaço contínuo para alocar o vetor, você deve duplicar a capacidade do vetor de bat-memória virtual. Você pode fazer isso criando um novo vetor e copiando (nos mesmos locais de memória) o vetor antigo. Assim, o vetor acima, depois de uma expansão, sempre permanecerá na posição 31, até que seja bat-liberado. Sempre será possível alocar após a expansão.
Bat-free
A operação de bat-free libera a memória correspondente a um vetor. Ela aparecerá no formato:
bat-free ENDEREÇO
Assim, seguindo o exemplo anterior, se o Batman te pedir para dar bat-free no endereço 31, você poderá usar as posições 31 a 36 (inclusive) livremente para alocar outras coisas depois. Perceba que isso irá deixar lacunas vazias na sua memória, mas no bat-computador isso não é um problema.
Depois da realização da operação de bat-free, pode ser que uma grande parte contígua do vetor fique livre. Para evitar desperdício, enquanto não houver bat-memória utilizada depois do primeiro quarto do vetor, ele deve ser reduzido pela metade. Por exemplo, se a memória tiver 128 inteiros e não houver nenhuma memória utilizada entre as posições 32 a 127 (inclusive), o vetor deve ser reduzido a 64 inteiros.
Atenção: a memória nunca deve ser reduzida a menos que 8 inteiros. Além disso, Batman, como grande programador que é, nunca erra os endereços passados para você. Assim, todo bat-free possui um alvo válido (que foi alocado e ainda não liberado).
Depois desta operação, não é necessário imprimir nada.
Bat-print
A operação de bat-print imprime a lista de inteiros indicada pelo seu início. Ela aparecerá no formato:
bat-print ENDEREÇO
Os inteiros são impressos separados por espaço e apenas os inteiros correspondentes ao vetor alocado naquela posição devem ser impressos. Lembre-se de que a posição apontada pelo endereço contém o tamanho do vetor.
Exemplo de saída:
10 12 16 18 42
Bat-uso
A operação de bat-uso imprime uma linha com dois inteiros: a quantidade de inteiros da memória que estão sendo usados (seja pela alocação ou por representarem o tamanho do vetor) e o tamanho total da bat-memória. A operação corresponde a uma linha sozinha, sem parâmetros:
bat-uso
Uma saída possível é:
321 de 512
Exemplo 1
Entrada
6
bat-alloc 5 1 2 3 4 5
bat-print 0
bat-alloc 3 9 8 7
bat-print 6
bat-free 0
bat-uso
Saída
0
1 2 3 4 5
6
9 8 7
4 de 16
Exemplo 2
Entrada
8
bat-alloc 7 52 57 63 25 33 40 37
bat-alloc 6 69 35 62 49 44 44
bat-free 0
bat-alloc 4 36 45 59 66
bat-uso
bat-alloc 3 24 34 51
bat-print 0
bat-uso
Saída
0
8
0
12 de 16
15
36 45 59 66
16 de 32
Dicas
Um aviso do Alfred: enquanto Bruce Wayne é despreocupado e irresponsável, Batman é frio, determinado e implacável. Não seja como Bruce Wayne! Antes de começar a programar, tome um tempo para pensar nas estruturas de dados utilizadas e nos algoritmos necessários para cada operação. Se começar a escrever código antes disso, pode ser que você gaste muito mais tempo do que necessário.
Em cada momento da execução do bat-computador, a memória conterá posições livres e posições ocupadas, então o vetor dinâmico corresponderá a uma sequência de blocos contíguos de posições livres ou ocupadas. Faça um desenho de um vetor de exemplo e se pergunte de que maneira ele seria representado em seu programa. Pode ser útil também criar uma função que liste e mostre os blocos atuais do vetor. Mesmo que ela não seja chamada na versão final do programa, funções como essa servem para inspecionar a estrutura de dados depois de cada operação e são convenientes durante o desenvolvimento.
Critérios
Você deve representar a memória virtual utilizando um vetor dinâmico de
inteiros que crescem e diminuem de acordo com as regras acima. Para alocar e
liberar memória você deverá usar malloc
e free
, mas outras operações sobre
ao vetor devem ser implementadas diretamente. Assim, não serão permitidas outras
funções para manipular memória, como realloc
, memcpy
, etc.