INF1018 - Software Básico (2016.1)
Primeiro Trabalho

Armazenamento de estruturas de inteiros

O objetivo do trabalho é implementar, na linguagem C, duas funções: code e decode. A função code escreve, em um arquivo binário, o conteúdo de estruturas de dados que contém campos inteiros de 32 e 64 bits, codificado conforme o formato fornecido adiante. A função decode permite visualizar o conteúdo de estruturas armazenadas em um arquivo gerado por code.


Leia com atenção o enunciado do trabalho e as instruções para a entrega. Em caso de dúvidas, não invente. Pergunte!

Função code

int code (char* desc, void* v, FILE* f); 
A função code recebe como argumentos: A função deverá retornar 0 em caso de sucesso, e -1 em caso de erro. Apenas erros de E/S (ou seja, erros na gravação do arquivo) devem ser considerados. Assuma que todos os parâmetros fornecidos à função estão corretos (isto é, a estrutura está corretamente especificada por desc, v contém o endereço da estrutura e f contém o descritor de um arquivo aberto, com sucesso, pela função que chamou code).

A função code não deve fechar o arquivo de saída. Como várias estruturas poderão ser armazenadas em um mesmo arquivo (i.e, poderá haver várias chamadas para code), apenas a função que abriu o arquivo (provavelmente, a main) deve fechar o arquivo.

A string desc representa, na ordem, o tipo de cada campo da struct, de acordo com o código a seguir:

    'i' - int (inteiro com sinal, 32 bits)
    'l' - long (inteiro com sinal, 64 bits)

Como exemplo, dada a declaração:

  struct t {
    int i1;
    int i2;
    long l1;
    long l2;
  };
  struct t teste;
a string desc correspondente é "iill".

Assumindo que o descritor do arquivo de saída está armazenado em uma variável arq, do tipo FILE*, a chamada para a gravação do conteúdo da estrutura teste neste arquivo seria:

   res = code("iill", &teste, arq);
Atenção! Para acessar os valores dos campos da estrutura armazenados na memória, a função code deve levar em consideração as regras de alinhamento especificadas para o ambiente onde ela será executada (SO Linux, em uma máquina de 64 bits)


Formato do arquivo gerado

Para cada estrutura, devem ser armazenadas, em sequência, as seguintes informações:

Vejamos agora um exemplo de codificação completa. Suponha a seguinte estrutura:

struct t {
  int i1;
  int i2;
  long l1;
  long l2;
} teste = {1, -1, 256, -256};
O conteúdo codificado dessa estrutura, começando com a marca de início, é
ff 81 02 81 01 82 80 04 02 ff 03


Função decode

int decode (FILE *f); 

A função decode permite a visualização, na saída padrão, do conteúdo de um arquivo binário com o formato descrito anteriormente (isto é, um arquivo gerado por chamadas a code). Essa saída pode ser gerada, por exemplo, através de chamadas a printf.

O único argumento de decode é o descritor de um arquivo aberto para leitura, em modo binário.

A função deverá retornar 0 em caso de sucesso, e -1 em caso de erro. Apenas erros de E/S (i.e, erros na leitura do arquivo), deverão ser detetados. A função decode pode assumir que o conteúdo do arquivo foi gerado corretamente.

A função decode não deve fechar o arquivo. Isso deverá ser feito pela função que abriu o arquivo (provavelmente, a main).

A saída da função decode, para cada estrutura armazenada, deve ser a seguinte

Como exemplo, para um arquivo que armazene a estrutura teste, descrita anteriormente, a saída de decode deveria ser:
---------------------------
Estrutura 1
<int> 1
<int> -1
<long> 256
<long> -256

DICA: Para obter o valor de inteiro com sinal n mapeado pela codificação em zigzag para um um inteiro sem sinal z você pode fazer:

  n = z >>  1;
  if (z & 0x01) /* negativos sempre mapeados para impares! */
    n = ~n;


Implementação e Execução

Você deve criar um arquivo fonte chamado code.c contendo as duas funções descritas acima (code e decode) e funções auxiliares, se for o caso. Esse arquivo não deve conter uma função main!

Crie também um arquivo code.h , que deve conter apenas os protótipos das funções code e decode.

Para testar seu programa, crie um outro arquivo, por exemplo, teste.c , contendo a função main.

Note que é responsabilidade da função main abrir o arquivo a ser gravado (por code) ou lido (por decode). O descritor do arquivo aberto será passado, como parâmetro, para essas funções.

Crie seu programa executável, teste, com a linha:

gcc -Wall -o teste code.c teste.c

Tanto o arquivo code.c como teste.c devem conter a linha:

#include "code.h"


Dicas

Implemente seu trabalho por partes, testando cada parte implementada antes de prosseguir.

Por exemplo, você pode implementar primeiro a codificação de estruturas e gravação do arquivo. Comece implementando casos simples (estruturas com apenas um campo, estruturas sem paddings), e vá introduzindo mais tipos de campos e valores à medida que os casos anteriores estejam funcionando.

Para verificar o conteúdo do arquivo gravado, você pode usar o utilitário hexdump. Por exemplo, o comando

hexdump -C <nome-do-arquivo>
exibe o conteúdo do arquivo especificado byte a byte, em hexadecimal (16 bytes por linha). A segunda coluna de cada linha (entre '|') exibe os caracteres ASCII correspondentes a esses bytes, se eles existirem.

Para abrir um arquivo para gravação ou leitura em formato binário, use a função

FILE *fopen(char *path, char *mode);
descrita em stdio.h. Seus argumentos são: A letra 'b', que indica o modo binário, é ignorada em sistemas como Linux, que tratam da mesma forma arquivos de tipos texto e binário. Mas ela é necessária em outros sistemas, como Windows, que tratam de forma diferente arquivos de tipos texto e binário (interpretando/modificando, por exemplo, bytes de arquivos "texto" que correspondem a caracteres de controle).

Para fazer a leitura e gravação do arquivo, uma sugestão é pesquisar as funções fwrite/fread e fputc/fgetc.


Entrega

Devem ser entregues via Moodle dois arquivos:

  1. o arquivo fonte code.c

    Coloque no início do arquivo fonte, como comentário, os nomes dos integrantes do grupo, da seguinte forma:

      /* Nome_do_Aluno1 Matricula Turma */
      /* Nome_do_Aluno2 Matricula Turma */
    
    Lembre-se que este arquivo não deve conter a função main!
  2. um arquivo texto, chamado relatorio.txt, descrevendo os testes realizados, o que está funcionando e, eventualmente, o que não está funcionando. Mostre exemplos de estruturas testadas (casos de sucesso e insucesso, se houver)!

    Você não deve explicar a sua implementação neste relatório. Seu programa deve ser suficientemente claro e bem comentado.

    Coloque também no relatório o nome dos integrantes do grupo.

Coloque na área de texto da tarefa do Moodle os nomes e turmas dos integrantes do grupo.

Para grupos de alunos da mesma turma, apenas uma entrega é necessária (usando o login de um dos integrantes do grupo).


Prazo


Observações