quinta-feira, 28 de dezembro de 2017

Problema da alocação dinâmica de memória com operações sobre conjuntos


Compartilhe!

Neste laboratório você deve implementar funções que realizam operações sobre conjuntos com tamanho máximo indeterminado.

Como o tamanho máximo dos conjuntos são indeterminados, usaremos alocação dinâmica para alterar o tamanho dos conjuntos em tempo de execução. Os conjuntos serão representados por um vetor e dois inteiros, tamanho e capacidade. O tamanho indica a quantidade de elementos que o conjunto contém e capacidade indica o tamanho do vetor alocado para o conjunto. Desta forma o conjunto consegue armazenar até capacidade elementos. Se o vetor ficar cheio e novos elementos precisarem ser inseridos no conjunto, temos que realocá-lo para um vetor maior. De forma similar, se muitos elementos forem removidos de um conjunto, o vetor alocado deverá ser diminído para ocupar menos espaço em memória.

O objetivo deste laboratório é criar uma biblioteca de funções em C em um arquivo separado para realizar operações sobre conjuntos de números inteiros. Os conjuntos serão representados utilizando-se vetores.

Aqui serão implementadas funções que realizem as seguintes operações:

Ordena: ordena o conjunto especificado de maneira crescente.
Pertence: verifica se um elemento pertence ao conjunto especificado, retornando verdadeiro ou falso. 
Continência: verifica se um conjunto está contido em outro conjunto retornando verdadeiro ou falso. 
Inicialização: inicializa um conjunto com tamanho zero e capacidade dois (02). 
Adição: adiciona um elemento em um conjunto, alterando o conjunto com a adição do novo elemento caso ele já não pertença ao mesmo.

Cada conjunto apresenta um tamanho atual e a capacidade que devem ser atualizados a medida que novos elementos forem adicionados. Caso a quantidade de elementos do conjunto (tamanho) seja igual a capacidade do vetor alocado e um novo elemento tenha que ser inserido, então o vetor do conjunto terá de ser realocado com o dobro de sua capacidade atual. (Deve-se realocar o vetor para obter mais memória.)

Subtração: remove um elemento de um conjunto, alterando o conjunto especificado com a remoção do elemento caso ele pertença ao conjunto.

Cada conjunto apresenta um tamanho atual e a capacidade que devem ser atualizados a medida que elementos forem removidos. Caso a capacidade do conjunto seja maior que dois (02) e a quantidade de elementos do conjunto seja menor ou igual que 1/4 da sua capacidade, então o vetor deverá ser realocado com a metade de sua capacidade (Deve-se realocar o vetor para reduzir o consumo de memória.)

União: faz a união de dois conjuntos, criando um conjunto com os elementos dos dois conjuntos de entrada. 
Interseção: faz a interseção de dois conjuntos, criando um conjunto com os elementos que pertencem aos dois conjuntos de entrada. 
Diferença: faz a diferença de dois conjuntos, criando um conjunto com os elementos do primeiro que não se encontram no segundo.

(Obs.: a descrição da atividade e código do arquivo main são de autoria dos PEDs do IC-Unicamp.)

Arquivo com a função main:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void ordena(int *conj, int tam);
int pertence(int *conj, int tam, int elemento);
int contido(int *conj_A, int *conj_B, int tam_A, int tam_B);
int* init(int *tam, int *cap);
int* adicao(int *conj, int *tam, int *cap, int elemento);
int* subtracao(int *conj, int *tam, int *cap, int elemento);
int* uniao(int *conj_A, int *conj_B, int tam_A, int tam_B, int *tam_C, int *cap_C);
int* intersecao(int *conj_A, int *conj_B, int tam_A, int tam_B, int *tam_C, int *cap_C);
int* diferenca(int *conj_A, int *conj_B, int tam_A, int tam_B, int *tam_C, int *cap_C);


/*
------------------------------------------------------------------------------------------------------------
void print(char comando[], int *conj, int tam, int cap);


Esta funcao imprime o comando junto com os dados do conjunto (elementos, tamanho e capacidade) que sofreu a acao.

Parametros:

- comando -> Comando no formato de string;
- conj -> Conjunto que sofreu a acao;
- tam -> Quantidade de elementos do conjunto;
- cap -> Capacidade do conjunto;


------------------------------------------------------------------------------------------------------------
*/

void print(char comando[], int *conj, int tam, int cap) {
  int i;

  /* Conjunto e ordenado antes de imprimir */
  ordena(conj, tam);

  /* Dados do conjunto */
  printf("%s{", comando);

  for(i = 0; i < tam; i++) {
    (i == 0) ? printf("%d", *(conj + i)) : printf(", %d", *(conj + i));
  }

  printf("}, tamanho = %d, capacidade = %d\n", tam, cap);
}

/*
------------------------------------------------------------------------------------------------------------
int* atribuicao(char *str, int *conj, int *tam ,int *cap);


Esta funcao adiciona o valores numericos de um comando em um conjunto.

Parametros:

- str -> Valores numericos de um comando separados por virgula;
- conj -> Conjunto que tera seus valores alterados;
- tam -> Quantidade de elementos do conjunto;
- cap -> Capacidade do conjunto;


Retorno

- Um ponteiro para o conjunto.


------------------------------------------------------------------------------------------------------------
*/

int* atribuicao(char *str, int *conj, int *tam ,int *cap) {
  char *temp = NULL;
  unsigned long i = 0;
  unsigned long j = 0;

  temp = (char*) malloc((strlen(str) + 1) * sizeof(char));
  strcpy(temp, str);
  conj = init(tam, cap);

  for (i = 0, j = 0; i <= strlen(str); i++){

    if(*(str + i) == ',' || *(str + i) == '\0') {
      *(temp + j) = '\0';

      /* Elementos sao adicionados no conjunto */
      if(j > 0) { 
        conj = adicao(conj, tam, cap, atoi(temp));
      }

      j = 0;
    } else {
      *(temp + j) = *(str + i);
      j++;
    }

  }

  free(temp);
  return conj;
}

/*
------------------------------------------------------------------------------------------------------------
char* strftr(char *str);


Esta funcao extrai os valores numericos presentes em um comando e os separa por virgula.

Parametros:

- str -> Comando dado como entrada em formato de string;

Retorno

- Uma string com os numeros separados por virgula.


------------------------------------------------------------------------------------------------------------
*/

char* strftr(char *str) {
  char filter[] = "-,0123456789";
  char *temp = NULL;
  unsigned long i = 0;
  unsigned long j = 0;

  temp = (char*) malloc((strlen(str) + 1) * sizeof(char));
  strcpy(temp, str);

  for(i = 0; i < strlen(str); i++) {

    /* Filtragem de dados numericos */
    if(strchr(filter, *(str + i)) != NULL) {
      *(temp + j) = *(str + i);
      j++;
    }

  }

  *(temp + j) = '\0';
  return temp;
}


/*
------------------------------------------------------------------------------------------------------------
void parse(char *str, int **A, int **B, int *tam_A ,int *tam_B ,int *cap_A ,int *cap_B);


Esta funcao interpetra o comando dado como entrada e dispara a acao correspondente.

Parametros:

- str -> Comando dado como entrada em formato de string;
- A -> Conjunto C;
- B -> Conjunto B;
- tam_A -> Quantidade de elementos do conjunto A;
- tam_B -> Quantidade de elementos do conjunto B;
- cap_A -> Capacidade do conjunto A;
- cap_B -> Capacidade do conjunto B;

------------------------------------------------------------------------------------------------------------
*/

void parse(char *str, int **A, int **B, int *tam_A ,int *tam_B ,int *cap_A ,int *cap_B) {
  char *temp = NULL;
  char *i = NULL;
  int *C = NULL;
  int tam_C = 0;
  int cap_C = 0;

  /* Identificacao do comando de atribuicao */
  if((i = strstr(str, " = ")) != NULL) {
    temp = strftr(i + strlen(" = "));

    if(strstr(str, "A") != NULL) {
      *A = atribuicao(temp, *A, tam_A, cap_A);
      print("A = ", *A, *tam_A, *cap_A);
    } else {
      *B = atribuicao(temp, *B, tam_B, cap_B);
      print("B = ", *B, *tam_B, *cap_B);
    }

    free(temp);
  }

  /* Identificacao do comando de adicao */
  if((i = strstr(str, " += ")) != NULL) {
    temp = strftr(i + strlen(" += "));

    if(strstr(str, "A") != NULL) {
      *A = adicao(*A, tam_A, cap_A, atoi(temp));
      print("A = ", *A, *tam_A, *cap_A);
    } else {
      *B = adicao(*B, tam_B, cap_B, atoi(temp));
      print("B = ", *B, *tam_B, *cap_B);
    }

    free(temp);
  }

  /* Identificacao do comando de remocao */
  if((i = strstr(str, " -= ")) != NULL) {
    temp = strftr(i + strlen(" -= "));

    if(strstr(str, "A") != NULL) {
      *A = subtracao(*A, tam_A, cap_A, atoi(temp));
      print("A = ", *A, *tam_A, *cap_A);
    } else {
      *B = subtracao(*B, tam_B, cap_B, atoi(temp));
      print("B = ", *B, *tam_B, *cap_B);
    }

    free(temp);
  }

  /* Identificacao do comando de pertence */
  if((i = strstr(str, " e ")) != NULL) {
    temp = strftr(str);

    if(strstr(str, "A") != NULL) {
      printf("%s\n", pertence(*A, *tam_A, atoi(temp)) ? "verdadeiro" : "falso");
    } else {
      printf("%s\n", pertence(*B, *tam_B, atoi(temp)) ? "verdadeiro" : "falso");
    }

    free(temp);
  }

  /* Identificacao do comando de contido */
  if((i = strstr(str, " c ")) != NULL) {

    if(strstr(str, "A") < strstr(str, "B")) {
      printf("%s\n", contido(*A, *B, *tam_A, *tam_B) ? "verdadeiro" : "falso");
    } else {
      printf("%s\n", contido(*B, *A, *tam_B, *tam_A) ? "verdadeiro" : "falso");
    }

  }

  /* Identificacao do comando de uniao */
  if((i = strstr(str, " u ")) != NULL) {
    C = uniao(*A, *B, *tam_A, *tam_B, &tam_C, &cap_C);

    if(strstr(str, "A") < strstr(str, "B")) {
      print("A u B = ", C, tam_C, cap_C);
    } else {
      print("B u A = ", C, tam_C, cap_C);
    }
    
    free(C);
  }

  /* Identificacao do comando de intersecao */
  if((i = strstr(str, " ^ ")) != NULL) {
    C = intersecao(*A, *B, *tam_A, *tam_B, &tam_C, &cap_C);

    if(strstr(str, "A") < strstr(str, "B")) {
      print("A ^ B = ", C, tam_C, cap_C);
    } else {
      print("B ^ A = ", C, tam_C, cap_C);
    }
    
    free(C);
  }

  /* Identificacao do comando de diferenca */
  if((i = strstr(str, " \\ ")) != NULL) {
    
    if(strstr(str, "A") < strstr(str, "B")) {
      C = diferenca(*A, *B, *tam_A, *tam_B, &tam_C, &cap_C);
      print("A \\ B = ", C, tam_C, cap_C);
    } else {
      C = diferenca(*B, *A, *tam_B, *tam_A, &tam_C, &cap_C);
      print("B \\ A = ", C, tam_C, cap_C);
    }

    free(C);
  }

}

int main() {
  int *A = NULL;
  int *B = NULL;
  int tam_A = 0;
  int tam_B = 0;
  int cap_A = 0;
  int cap_B = 0;
  char *str = NULL;
  char c = ' ';
  int size = 0;
  int capacity = 0;

  /* Enquanto o comando de sair (Q) nao for dado sera lido novos comando */
  do {
    size = 0;
    /* Novos comandos sao lidos ate que uma quebra de lina seja identificada */
    while(scanf("%c", &c) && c != '\n') {
      if(str == NULL) {
        capacity = 2;
        str = (char*) malloc(capacity * sizeof(char));
      }
      if(size + 1 == capacity) {
        capacity *= 2;
        str = (char*) realloc(str, capacity * sizeof(char));
      }
      str[size] = c;
      size++;
    }
    str[size] = '\0';
    /* Interpretacao do comando */
    parse(str, &A, &B, &tam_A , &tam_B , &cap_A , &cap_B);
  } while(strcmp(str, "Q"));

  /* Dados desalocados */
  if(str) free(str);
  if(A) free(A);
  if(B) free(B);
}

Arquivo, de minha autoria, com as funções requeridas pela main acima:

/*
------------------------------------------------------------------------------------------------------------
void ordena(int *conj, int tam);
Esta funcao deve ordenar um conjunto dado como parametro.
Parametros:
- conj -> Ponteiro para o conjunto;
- tam -> Quantidade de elementos do conjunto;
------------------------------------------------------------------------------------------------------------
*/
void ordena(int * conj, int tam) {

 int aux;

 for (int i = 0; i < tam - 1; i++)//Repita tamanho-1 vezes:
  for (int j = 0; j < tam - 1; j++)//No "pior" dos casos, deve "levar" o maior número da primeira posição até a última.
   if (conj[j] > conj[j + 1]) {//O método de ordenação utilizado é o bubble sort:

    aux = conj[j];
    conj[j] = conj[j + 1];
    conj[j + 1] = aux;

   }

}

/*
------------------------------------------------------------------------------------------------------------
int pertence(int *conj, int tam, int elemento);
Esta funcao deve verificar se um elemento esta presente no conjunto.
Parametros:
- conj -> Ponteiro para o conjunto;
- tam -> Quantidade de elementos do conjunto;
- elemento -> Elemento no qual deve ser ser verificado se esta presente no conjunto;

Retorno
- 1 Caso o elemento PERTENCA conjunto;
- 0 Caso o elemento NAO PERTENCA ao conjunto;
------------------------------------------------------------------------------------------------------------
*/
int pertence(int * conj, int tam, int elemento) {

 //Percorro o conjunto comparando com o elemento. Se encontrar um que seja igual, retorna 1 e fim. Se percorro o conjunto todo e não retorno nada, é porque não encontrei elemento igual, logo retorno 0.
 for (int i = 0; i < tam; i++)
  if (conj[i] == elemento) return 1;

 return 0;
}


/*
------------------------------------------------------------------------------------------------------------
int contido(int *conj_A, int *conj_B, int tam_A, int tam_B);

Esta funcao deve verificar se o conjunto A esta contido no conjunto B.
Parametros:
- conj_A -> Ponteiro para o conjunto A;
- conj_B -> Ponteiro para o conjunto B;
- tam_A -> Quantidade de elementos do conjunto A;
- tam_B -> Quantidade de elementos do conjunto B;

Retorno
- 1 Caso o conjunto A ESTEJA CONTIDO no conjunto B;
- 0 Caso o conjunto A NAO ESTEJA CONTIDO no conjunto B;
------------------------------------------------------------------------------------------------------------
*/
int contido(int * conj_A, int * conj_B, int tam_A, int tam_B) {

 if (tam_A > tam_B) //Se tamanho do conj_A for maior que o do conj_B, conj_A não pode estar contido em conj_B.
  return 0;

 for (int i = 0; i < tam_A; i++) //Percorro todos os elementos do conj_A. Se algum não pertencer ao conj_B, ele não está contido.
  if (!pertence(conj_B, tam_B, conj_A[i]))
   return 0;


 //Retorna 1 também caso conj_A seja {}, pois o programa não entra no for anterior no entanto deve retornar verdadeiro para conj_A estar contido em conj_B. E, caso entrou no for anterior e não encontrou elemento em conj_A que não pertencente a conj_B, deve também retornar 1.
 return 1;

}


/*
------------------------------------------------------------------------------------------------------------
int* init(int *tam, int *cap);

Esta funcao deve inicializar um vetor(conjunto).
Parametros:
- tam -> Ponteiro para a quantidade de elementos do conjunto;
- cap -> Ponteiro para a capacidade de elementos do conjunto;

OBS:
- A capacidade inicial do vetor(conjunto) deve ser 2.
- O quantidade inicial de elementos no vetor(conjunto) deve ser zero.
- Nao confundir capacidade com quantidade de elementos.

Retorno
- Ponteiro para o conjunto;
------------------------------------------------------------------------------------------------------------
*/
int * init(int * tam, int * cap) {

 int * p;

 p = malloc(2 * sizeof(int));//Aloca espaço para dois inteiros,
 * tam = 0, * cap = 2;//e atualiza as variáveis para as quais tam (tamanho) e cap (capacidade) apontam.

 return p;
}


/*
------------------------------------------------------------------------------------------------------------
int* adicao(int *conj, int *tam, int *cap, int elemento);

Esta funcao deve adicionar um novo elemento no conjunto, ou seja, se o elemento ja pertence ao 
conjunto o mesmo NAO deve ser adicionado.

Parametros:
- conj -> Ponteiro para o conjunto;
- tam -> Ponteiro para a quantidade de elementos do conjunto;
- cap -> Ponteiro para a capacidade de elementos do conjunto;
- elemento -> Elementos para ser adicionado;

OBS:
- Ao adicionar um novo elemento o tamanho atual do conjunto devera ser atualizado;
- Caso o ponteiro para o conjunto seja NULL, o conjunto devera ser alocado com uma capacidade de dois (02) e a atualizacao da capacidade devera ser feita;
- Caso a quantidade de elementos do conjunto seja igual a capacidade do conjunto e um novo elemento tenha que 
ser inserido, então o conjunto tera de ser realocado com o dobro da capacidade e a capcidade do conjunto 
devera ser atualizada;

Retorno
- Ponteiro para o conjunto;
------------------------------------------------------------------------------------------------------------
*/
int * adicao(int * conj, int * tam, int * cap, int elemento) {

 int * aux;

 if (conj == NULL)
  conj = init(tam, cap);//Aloca um conjunto caso o apontador conj seja NULL.
 else {

  //Se já houver esse elemento no conjunto, não o adicionarei de novo, logo retorno o ponteiro do próprio conjunto sem alteração.
  if (pertence(conj, * tam, elemento))
   return conj;
 }

 //Se não entrei no segundo if, significa que ainda não há esse elemento no conjunto:
 if ( * tam == * cap) {//Se conj estiver cheio:

  aux = malloc(2 * ( * tam) * sizeof(int));//Aloco um novo vetor maior,

  for (int i = 0; i < * tam; i++)//Transfiro os elementos de conj para esse novo vetor maior,
   aux[i] = conj[i];

  free(conj);//Libero na memória o espaço apontado pelo conj,

  *cap = 2 * ( *tam);//Atualizo a capacidade,

  conj = aux;//Por fim, faço o apontador conj apontar para o espaço alocado por aux, que é um espaço maior.

 }


 conj[ * tam] = elemento;//Depois de considerar todas as situações acima, adiciono o elemento e atualizo seu tamanho.
 ( * tam) ++;

 return conj; //E então retorno o conjunto.

}


/*
------------------------------------------------------------------------------------------------------------
int* subtracao(int *conj, int *tam, int *cap, int elemento);

Esta funcao deve remover um elemento no conjunto caso ele exista.
Parametros:
- conj -> Ponteiro para o conjunto;
- tam -> Ponteiro para a quantidade de elementos do conjunto;
- cap -> Ponteiro para a capacidade de elementos do conjunto;
- elemento -> Elementos para ser removido;

OBS:
- Ao remover um elemento o tamanho atual do conjunto devera ser atualizado;
- Caso a capacidade seja maior que dois (02) e a quantidade de elementos do conjunto seja menor ou igual que 1/4 da capacidade do conjunto, então 
o conjunto tera de ser realocado com a metade da capacidade e a capacidade do conjunto devera ser atualizada.

Retorno
- Ponteiro para o conjunto;
------------------------------------------------------------------------------------------------------------
*/
int * subtracao(int * conj, int * tam, int * cap, int elemento) {

 int * aux;

 for (int i = 0; i < * tam; i++) //Percorro o vetor procurando pelo elemento a ser removido.
  if (conj[i] == elemento) { //Quando o encontro, sigo com outro for para "trazer para frente" os elementos seguintes.
   for (int j = i; j < * tam; j++)
    conj[j] = conj[j + 1];

   ( * tam) --; //Registro que um elemento foi removido.

   if ( * tam <= (( * cap) / 4) && *cap > 2) {//Verifico se tamanho é menor ou igual a 1/4 da capacidade e se capacidade é maior que 2


    aux = malloc(( * cap) / 2 * sizeof(int));//Aloco um espaço com a metade da capacidade,

    for (int i = 0; i < * tam; i++)//Transfiro os elementos de conj para esse novo vetor menor,
     aux[i] = conj[i];

    free(conj);//Libero na memória o espaço apontado pelo conj,

    * cap = ( * cap) / 2;//Atualizo a capacidade,
    conj = aux; //Por fim, faço o apontador conj apontar para o espaço alocado por aux, que é um espaço menor.

   }

   break;
  }

 return conj; //Retorna o conjunto.

}


/*
------------------------------------------------------------------------------------------------------------
int* uniao(int *conj_A, int *conj_B, int tam_A, int tam_B, int *tam_C, int *cap_C);

Esta funcao deve computar a uniao entre os conjuntos A e B. O resultado dessa uniao deve ser armazenado em 
um novo conjunto C.

Parametros:

- conj_A -> Ponteiro para o conjunto A;
- conj_b -> Ponteiro para o conjunto B;
- tam_A -> Quantidade de elementos do conjunto A;
- tam_B -> Quantidade de elementos do conjunto B;
- tam_C -> Ponteiro para a quantidade de elementos do conjunto resultante;
- cap_C -> Ponteiro para a capacidade de elementos do conjunto resultante;


OBS:
- O tamanho atual e a capacidade do conjunto resultante C devera seguir a logica apresentada nas funcoes init e adicao.
- Os valores de quantidade de elementos e capacidade do conjunto resultante C devem ser atualizadosrespectivamente nos parametros tam_C e cap_C.

Retorno
- Ponteiro para o conjunto C;
------------------------------------------------------------------------------------------------------------
*/
int * uniao(int * conj_A, int * conj_B, int tam_A, int tam_B, int * tam_C, int * cap_C) {

 int * conj_C;

 conj_C = init(tam_C, cap_C);//Aloca um espaço para conj_C.

 for (int i = 0; i < tam_A; i++)//Adiciona em conj_C o que estiver em conj_A e que não já esteja nele.
  conj_C = adicao(conj_C, tam_C, cap_C, conj_A[i]);//Caso já esteja em conj_C, a adicao() retornará o apontador para conj_C sem alteração.

 for (int i = 0; i < tam_B; i++)//O mesmo procedimento feito com o conj_A.
  conj_C = adicao(conj_C, tam_C, cap_C, conj_B[i]);

 return conj_C;//Retorna o apontador para o conjunto união.

}


/*
------------------------------------------------------------------------------------------------------------
int* intersecao(int *conj_A, int *conj_B, int tam_A, int tam_B, int *tam_C, int *cap_C);

Esta funcao deve computar a intersecao entre os conjuntos A e B. O resultado dessa intersecao deve ser armazenada em um novo conjunto C.

Parametros:
- conj_A -> Ponteiro para o conjunto A;
- conj_B -> Ponteiro para o conjunto B;
- tam_A -> Quantidade de elementos do conjunto A;
- tam_B -> Quantidade de elementos do conjunto B;
- tam_C -> Ponteiro para a quantidade de elementos do conjunto resultante;
- cap_C -> Ponteiro para a capacidade de elementos do conjunto resultante;

OBS:
- O tamanho atual e a capacidade do conjunto resultante C devera seguir a logica apresentada nas funcoes init e adicao.
- Os valores de quantidade de elementos e capacidade do conjunto resultante C devem ser atualizados respectivamente nos parametros tam_C e cap_C.

Retorno
- Ponteiro para o conjunto C;
------------------------------------------------------------------------------------------------------------
*/


int * intersecao(int * conj_A, int * conj_B, int tam_A, int tam_B, int * tam_C, int * cap_C) {

 int * conj_C;

 conj_C = init(tam_C, cap_C);//Aloca um espaço para conj_C.

 for (int i = 0; i < tam_A; i++)//Adiciona em conj_C o que de conj_A estiver em conj_B e que não já esteja nele.
  if (pertence(conj_B, tam_B, conj_A[i]))
   conj_C = adicao(conj_C, tam_C, cap_C, conj_A[i]);//Caso já esteja em conj_C, a adicao() retornará o apontador para conj_C sem alteração.

 return conj_C;//Retorna o apontador para o conjunto interseção.

}


/*
------------------------------------------------------------------------------------------------------------
int* diferenca(int *conj_A, int *conj_B, int tam_A, int tam_B, int *tam_C, int *cap_C);

Esta funcao deve computar a diferenca entre os conjuntos A e B. O resultado dessa diferenca deve ser 
armazenada em um novo conjunto C.

Parametros:
- conj_A -> Ponteiro para o conjunto A;
- conj_b -> Ponteiro para o conjunto B;
- tam_A -> Quantidade de elementos do conjunto A;
- tam_B -> Quantidade de elementos do conjunto B;
- tam_C -> Ponteiro para a quantidade de elementos do conjunto resultante;
- cap_C -> Ponteiro para a capacidade de elementos do conjunto resultante;
- elemento -> Elementos para ser removido;

OBS:
- O tamanho atual e a capacidade do conjunto resultante C devera seguir a logica apresentada nas funcoes 
init e adicao.
- Os valores de quantidade de elementos e capacidade do conjunto resultante C devem ser atualizados 
respectivamente nos parametros tam_C e cap_C.

Retorno
- Ponteiro para o conjunto C;
------------------------------------------------------------------------------------------------------------
*/
int * diferenca(int * conj_A, int * conj_B, int tam_A, int tam_B, int * tam_C, int * cap_C) {

 int * conj_C;

 conj_C = init(tam_C, cap_C);//Aloca um espaço para conj_C.

 for (int i = 0; i < tam_A; i++)//Adiciona em conj_C o que de conj_A não estiver em conj_B e que não já esteja nele.
  if (!pertence(conj_B, tam_B, conj_A[i]))
   conj_C = adicao(conj_C, tam_C, cap_C, conj_A[i]);//Caso já esteja em conj_C, a adicao() retornará o apontador para conj_C sem alteração.


 return conj_C;//Retorna o apontador para o conjunto diferença.

}