Mostrando postagens com marcador vetor. Mostrar todas as postagens
Mostrando postagens com marcador vetor. Mostrar todas as postagens

terça-feira, 7 de outubro de 2014

String

Na linguagem C, uma string (ou cadeia de caracteres) é um vetor de caracteres em que o caractere nulo ('\0') é interpretado como fim da parte relevante do vetor.  

Exemplo:
    char *s;
    s = malloc( 10 * sizeof (char));
    s[0] = 'A';
    s[1] = 'B';
    s[2] = 'C';
    s[3] = '\0';
    s[4] = 'D';

Depois da execução desse fragmento de código, o vetor s[0..3] contém a cadeia de caracteres ABC. O caractere nulo marca o fim dessa cadeia. A porção s[4..9] do vetor é ignorada.

  • Para determinar o tamanho de uma string use a função strlen( ). Esta função faz parte do arquivo de cabeçalho string.h. Sua sintaxe é:


strlen(string)

Exemplo:


/* Determinando o tamanho de uma string usando
 * a função strlen() */

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

int main()
  {
    char string[20];

    printf("\n");
    printf("Determinando o tamanho de uma string\n");
    printf("------------------------------------\n");
    printf("\n");
    printf("Digite a string :");
    scanf("%s",&string);
    printf("\n");
    printf("A string tem %d carateres.\n\n",strlen(string));
    return(0);
  }

  • Para copiar uma string em outra use a função strcpy( ). Esta função faz parte do arquivo de cabeçalho string.h. Sua sintaxe é:

strcpy(destino, origem)
Exemplo:
/* Copiando uma string em outra usando a
 * função strcpy() */

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

int main()
  {
    char string1[10], string2[10];

    printf("\n");
    printf("Copiando uma string em outra\n");
    printf("----------------------------\n");
    printf("\n");
    printf("Digite string1 :");
    scanf("%s",&string1);
    printf("\n");
    printf("string1 = %s\n",string1);
    printf("string2 = %s\n",strcpy(string2,string1));
    return(0);
  }

Na prática, todo conteúdo de string2 é substituído por string1.

  • Para unir duas strings use a função strcat( ). Esta função faz parte do arquivo de cabeçalho string.h. Sua sintaxe é:

strcat(destino, origem)
Exemplo:
/* Unindo duas strings usando a
 * função strcat() */

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

int main()
  {
    char string1[100], string2[10];

    printf("\n");
    printf("Unindo duas strings\n");
    printf("-------------------\n");
    printf("\n");
    printf("Digite string1 :");
    scanf("%s",&string1);
    printf("\n");
    printf("Digite string2 :");
    scanf("%s",&string2);
    printf("\n");
    printf("Unindo string1 a string2 : %s\n\n",strcat(string2,string1));
    return(0);
  }

Manipulando STRINGS

A Linguagem C não possui o tipo de dado string, as strings em C são arrays de caracteres, além disso, não temos como fazer operações básicas usando os operadores como, por exemplo, comparar dois arrays usando o operador de igualdade como fazemos com o tipo inteiro. O que temos na Linguagem C para trabalhar com strings é um conjunto de funções que fazem essas operações. Para utilizar essas funções devemos sempre incluir o arquivo de cabeçalho string.h em nossos programas.

Abaixo serão apresentadas algumas funções para manipulação de strings, assim como a descrição e um exemplo do que cada função faz.


size_t strlen (const char *s)

Calcula o tamanho da string s.

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

int
main ()
{
  char str[100] = { 0 };
  int tam;

  printf ("Entre com uma frase (no máximo 100 caracteres) : ");
  scanf ("%[^\n]", str);

  tam = strlen (str);

  printf ("A string digitada tem %d caracteres\n", tam);

  return 0;
}

char *strcpy (char *destino, const char *origem)


A função strcpy copia a string origem para a string em destino.

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

int
main ()
{

  char origem[100];
  char destino[100];

  printf ("Entre com a string : ");
  scanf ("%[^\n]", origem);


/* copia string */
  strcpy (destino, origem);

  printf ("Origem : %s\n", origem);
  printf ("Destino : %s\n", destino);

  return 0;
}

char *strncpy (char *destino, const char *origem, size_t n)


A função strncpy copia no máximo n caracteres da string origem para a string destino.

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

int
main ()
{

  char *origem = "Manipulando Strings em C!";
  char destino[20];

/* copia string com até 19 caracteres */
  strncpy (destino, origem, 19);

  printf ("Origem : %s\n", origem);
  printf ("Destino : %s\n", destino);

  return 0;
}

char *strcat (char *s1, const char *s2)


Concatena o conteúdo da string s2 em s1.

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

int
main ()
{

  char s[100];

  printf ("Digite algo : ");
  scanf ("%[^\n]", s);

  /* concatena ... no final da string digitada */
  strcat (s, "...");

  printf ("%s\n", s);

  return 0;
}

char *strncat (char *s1, const char *s2, size_t n)


Concatena no máximo n caracteres da string s2 para o final da string s1.

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

int
main ()
{

  char s1[100];
  char *s2 = "..........";

  printf ("Digite algo : ");
  scanf ("%[^\n]", s1);

  /* Concatena 3 caracteres de s2 para o final de s1 */
  strncat (s1, s2, 3);

  printf ("%s\n", s1);

  return 0;
}

int strcmp (const char *s1, const char *s2)


Compara a string s1 com s2. A função retorna um int que será zero, caso s1 seja igual s2, menor que zero, caso s1 seja menor que s2, maior que zero, caso s1 seja maior que s2.

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

int
main ()
{

  char s1[100];
  char s2[100];
  int ret;

  printf ("Digite a string s1 : ");
  scanf ("%[^\n]", s1);
  fflush (stdin);

  printf ("Digite a string s2 : ");
  scanf ("%[^\n]", s2);

  ret = strcmp (s1, s2);

  if (ret == 0)
    {
      printf ("As strings digitadas são iguais\n");
    }
  else
    {
      printf ("As strings digitadas são diferentes\n");
    }

  printf ("s1 = %s\n", s1);
  printf ("s2 = %s\n", s2);
  printf ("retorno = %d\n", ret);

  return 0;
}

int strncmp (const char *s1, const char *s2, size_t n)


Compara até n caracteres da string s1 com s2. A função retorna um int que será zero, caso s1 seja igual s2, menor que zero, caso s1 seja menor que s2, maior que zero, caso s1 seja maior que s2.

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

int
main ()
{

  char s1[100];
  char s2[100];
  int ret;

  printf ("Digite a string s1 : ");
  scanf ("%[^\n]", s1);
  fflush (stdin);

  printf ("Digite a string s2 : ");
  scanf ("%[^\n]", s2);

  /* Compara até os 3 primeiros caracteres */
  ret = strncmp (s1, s2, 3);

  if (ret == 0)
    {
      printf ("As strings digitadas são iguais\n");
    }
  else
    {
      printf ("As strings digitadas são diferentes\n");
    }

  printf ("s1 = %s\n", s1);
  printf ("s2 = %s\n", s2);
  printf ("retorno = %d\n", ret);

  return 0;
}



Ponteiros

Como Funcionam os Ponteiros 

Os ints guardam inteiros. Os floats guardam números de ponto flutuante. Os chars guardam caracteres. Ponteiros guardam endereços de memória. Quando você anota o endereço de um colega você está criando um ponteiro. O ponteiro é este seu pedaço de papel. Ele tem anotado um endereço. Qual é o sentido disto? Simples. Quando você anota o endereço de um colega, depois você vai usar este endereço para achá-lo. O C funciona assim. Você anota o endereço de algo numa variável ponteiro para depois usar.

Da mesma maneira, uma agenda, onde são guardados endereços de vários amigos, poderia ser vista como sendo uma matriz de ponteiros no C. 

Um ponteiro também tem tipo. Veja: quando você anota um endereço de um amigo você o trata diferente de quando você anota o endereço de uma firma. Apesar de o endereço dos dois locais ter o mesmo formato (rua, número, bairro, cidade, etc.) eles indicam locais cujos conteúdos são diferentes. Então os dois endereços são ponteiros de tipos diferentes. 

No C quando declaramos ponteiros nós informamos ao compilador para que tipo de variável vamos apontá-lo. Um ponteiro int aponta para um inteiro, isto é, guarda o endereço de um inteiro. 

Declarando e Utilizando Ponteiros 

Para declarar um ponteiro temos a seguinte forma geral:

tipo_do_ponteiro *nome_da_variável; 

É o asterisco (*) que faz o compilador saber que aquela variável não vai guardar um valor mas sim um endereço para aquele tipo especificado. Vamos ver exemplos de declarações: 

int *pt;
char *temp,*pt2; 

O primeiro exemplo declara um ponteiro para um inteiro. O segundo declara dois ponteiros para caracteres. Eles ainda não foram inicializados (como toda variável do C que é apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na porção da memória reservada ao sistema operacional do computador. 

Usar o ponteiro nestas circunstâncias pode levar a um travamento do micro, ou a algo pior. O ponteiro deve ser inicializado (apontado para algum lugar conhecido) antes de ser usado! Isto é de suma importância! 

Para atribuir um valor a um ponteiro recém-criado poderíamos igualá-lo a um valor de memória. Mas, como saber a posição na memória de uma variável do nosso programa? Seria muito difícil saber o endereço de cada variável que usamos, mesmo porque estes endereços são determinados pelo compilador na hora da compilação e realocados na execução. Podemos então deixar que o compilador faça este trabalho por nós. Para saber o endereço de uma variável basta usar o operador &. Veja o exemplo: 

 int count=10; 
 int *pt; 
 pt=&count; 

Criamos um inteiro count com o valor 10 e um apontador para um inteiro pt. A expressão &count nos dá o endereço de count, o qual armazenamos em pt. Simples, não é? 

Repare que não alteramos o valor de count, que continua valendo 10. 

Como nós colocamos um endereço em pt, ele está agora "liberado" para ser usado. Podemos, por exemplo, alterar o valor de count usando pt. Para tanto vamos usar o operador "inverso" do operador &. É o operador *. No exemplo acima, uma vez que fizemos pt=&count a expressão *pt é equivalente ao próprio count. Isto significa que, se quisermos mudar o valor de count para 12, basta fazer *pt=12

- Que tal voltarmos à nossa analogia para ver o que está acontecendo. 

Digamos que exista uma firma. Ela é como uma variável que já foi declarada. Você tem um papel em branco onde vai anotar o endereço da firma. O papel é um ponteiro do tipo firma. Você então liga para a firma e pede o seu endereço, o qual você vai anotar no papel. Isto é equivalente, no C, a associar o papel à firma com o operador &. Ou seja, o operador &aplicado à firma é equivalente a você ligar para a mesma e pedir o endereço. Uma vez de posse do endereço no papel você poderia, por exemplo, fazer uma visita à firma. No C você faz uma visita à firma aplicando o operador * ao papel. Uma vez dentro da firma você pode copiar seu conteúdo ou modificá-lo. 

 Uma observação importante: apesar do símbolo ser o mesmo, o operador * (multiplicação) não é o mesmo operador que o * (referência de ponteiros). Para começar o primeiro é binário, e o segundo é unário pré-fixado.

 Aqui vão dois exemplos de usos simples de ponteiros:

#include <stdio.h> 
int main () 
 int num,valor; 
 int *p; 
 num=55; 
 p=&num; 
/* Pega o endereco de num */ 
 valor=*p; 
 /* Valor e igualado a num de uma maneira indireta */ 
 printf ("\n\n%d\n",valor); 
 printf ("Endereco para onde o ponteiro aponta: %p\n",p); 
 printf ("Valor da variavel apontada: %d\n",*p); 
 return(0); 
}

e:

#include <stdio.h> 
int main () 
 int num,*p; 
 num=55; 
 p=&num; /* Pega o endereco de num */ 
 printf ("\nValor inicial: %d\n",num); 
 *p=100; /* Muda o valor de num de uma maneira indireta */ 
 printf ("\nValor final: %d\n",num); 
 return(0); 
}

Nos exemplos acima vemos um primeiro exemplo do funcionamento dos ponteiros. No primeiro exemplo, o código %p usado na função printf() indica à função que ela deve imprimir um endereço. 

Podemos fazer algumas operações aritméticas com ponteiros. A primeira, e mais simples, é igualar dois ponteiros. Se temos dois ponteiros p1 e p2 podemos igualá-los fazendo p1=p2. Repare que estamos fazendo com que p1 aponte para o mesmo lugar que p2. Se quisermos que a variável apontada por p1 tenha o mesmo conteúdo da variável apontada por 41 p2 devemos fazer *p1=*p2. Basicamente, depois que se aprende a usar os dois operadores (& e *) fica fácil entender operações com ponteiros.
As próximas operações, também muito usadas, são o incremento e o decremento. Quando incrementamos um ponteiro ele passa a apontar para o próximo valor do mesmo tipo para o qual o ponteiro aponta. Isto é, se temos um ponteiro para um inteiro e o incrementamos ele passa a apontar para o próximo inteiro. Esta é mais uma razão pela qual o compilador precisa saber o tipo de um ponteiro: se você incrementa um ponteiro char* ele anda 1 byte na memória e se você incrementa um ponteiro double* ele anda 8 bytes na memória. O decremento funciona semelhantemente. Supondo que p é um ponteiro, as operações são escritas como: 

p++; 
p--; 

Mais uma vez insisto. Estamos falando de operações com ponteiros e não de operações com o conteúdo das variáveis para as quais eles apontam. Por exemplo, para incrementar o conteúdo da variável apontada pelo ponteiro p, faz-se: 

(*p)++; 

Outras operações aritméticas úteis são a soma e subtração de inteiros com ponteiros. Vamos supor que você queira incrementar um ponteiro de 15. Basta fazer: 

 p=p+15; ou p+=15; 

E se você quiser usar o conteúdo do ponteiro 15 posições adiante: 

 *(p+15); 

A subtração funciona da mesma maneira. Uma outra operação, às vezes útil, é a comparação entre dois ponteiros. Mas que informação recebemos quando comparamos dois ponteiros? Bem, em primeiro lugar, podemos saber se dois ponteiros são iguais ou diferentes (== e !=). No caso de operações do tipo >, <, >= e <= estamos comparando qual ponteiro aponta para uma posição mais alta na memória. Então uma comparação entre ponteiros pode nos dizer qual dos dois está "mais adiante" na memória. A comparação entre dois ponteiros se escreve como a comparação entre outras duas variáveis quaisquer: 

 p1>p2 

Há entretanto operações que você não pode efetuar num ponteiro. Você não pode dividir ou multiplicar ponteiros, adicionar dois ponteiros, adicionar ou subtrair floats ou doubles de ponteiros. 

Matrizes

Pode ser definido como sendo um conjunto de variáveis do mesmo tipo. São também conhecidos por vetores ou array. Sua declaração segue o mesmo formato de variáveis, porém acrescenta-se o número de elementos desejados entre colchetes ([ e ]).

Os elementos são enumerados de 0 (zero) até o número especificado como quantidade menos 1 (n-1). Para matrizes multidimensionais, são fornecidos tantos índices quanto o número de dimensões. Para referenciar uma das variáveis, devemos fornecer o índice do elemento entre colchetes. A linguagem C não verifica se o índice é inválido, permitindo a leitura de "lixo" e alteração de outras posições de memória.


A inicialização pode ser feita apenas na declaração. Se o número de elementos é menor que o tamanho do vetor, os elementos restante são inicializados com zero. Podemos omitir o tamanho do vetor quando ele é inicializado (mas somente quando inicializado!). No caso de matrizes de mais de uma dimensão, somente a primeira dimensão pode ser omitida.

int vet[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
float Mat3x3[3][3] = {{ 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }};
char nome[60] = “Curso de C Progressivo”;

Sintaxe:
tipo nome_matriz[linhas][colunas];
tipo nome_matriz[profundidade][linhas][colunas];

 Exemplos:
 float fNotas[50][2];
 int iMatriz[2][2][3]; 

 Referenciando elementos de uma matriz:

int iMatriz[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};


Na memória, é representado como:


Isso também pode ser feito para adquirirmos o formato de matriz que quisermos. Ou seja, se fosse uma matriz tridimensional, bastaria ter três dimensionadores, se fosse 4D, então, 4 dimensionadores, se fosse 5D (apesar de geometricamente difícil de imaginar) seriam 5 dimensionadores. E assim por diante...


domingo, 5 de outubro de 2014

Alocação de Memória

Ao criar um programa em “C” usualmente temos que especificar as variáveis que vamos usar antes de começar a executar o programa, reservando assim um espaço na memória. As variáveis que são alocadas em posições fixas da memória são chamadas de variáveis estáticas, e as variáveis que não possuem uma posição fixa e que são criadas e destruídas durante a execução do programa são chamadas de variáveis dinâmicas.


A alocação de memória no computador pode ser dividida em dois grupos principais:
  • Alocação Estática: os dados tem um tamanho fixo e estão organizados sequencialmente na memória do computador. Um exemplo típico de alocação estática são as variáveis globais, e os vetores (estáticos).
  • Alocação Dinâmica: os dados não precisam ter um tamanho fixo, pois podemos definir para cada dado quanto de memória que desejamos usar. Sendo assim vamos alocar espaços de memória (blocos) que não precisam estar necessariamente organizados de maneira sequencial, podendo estar distribuídos de forma esparsa na memória do computador. Na alocação dinâmica, vamos pedir para alocar/desalocar blocos de memória, de acordo com a nossa necessidade, reservando ou liberando blocos de memória durante a execução de um programa. Para poder “achar” os blocos esparsos na memória usamos as variáveis do tipo Ponteiro (indicadores de endereços de memória). As variáveis locais e os parâmetros passados por valor são alocados dinamicamente.
Neste post vou abordar apenas alocação estática, e no próximo falarei da alocação dinâmica.

Alocação Estática de Memória:


As estruturas de dados para armazenar um conjunto de dados (exemplo: um cadastro com vários registros), podem ser organizadas de formas diferentes, de acordo com a maneira que os dados são inseridos e retirados da memória do computador. As formas mais usuais são: as listas lineares sequenciais (vetores simples), as filas, as pilhas e os deques.

Listas lineares sequenciais:

 

Uma maneira interessante de manipular este tipo de estruturas de dados, é através da construção de um conjunto de rotinas genéricas e modulares de manipulação de listas sequenciaisVamos aqui descrever uma possível proposta de um conjunto de rotinas empregadas para este fim. 

Rotinas básicas de manipulação de Vetores:
- Estruturas de dados com alocação estática
- Inserção no final do vetor
- Remoção lógica dos elementos

Aplicação típica:
- Pequenos cadastros

Estrutura de dados: 
         typedef int Tipo_Dado;
         typedef struct {
                                 Tipo_Dado Dado [MAX_VETOR];
                                 int Excluido [MAX_VETOR];
                                 int Inicio, Fim;
         } Tipo_Vetor;

Rotinas:
  void inicializa_vetor (Tipo_Vetor *V);
  int insere_vetor (Tipo_Vetor *V; Tipo_Dado Dado);
  int consulta_vetor (Tipo_Vetor V; int Indice; Tipo_Dado *Dado);
  int acha_vetor (Tipo_Vetor V; Tipo_Dado Dado; int *Indice);
  void lista_vetor (Tipo_Vetor V);
  int exclui_vetor (Tipo_Vetor *V; int Indice);
  int atualiza_vetor (Tipo_Vetor *V; int Indice; Tipo_Dado Novo_Dado);
  void compacta_vetor (Tipo_Vetor *V);
  int vazio_vetor (Tipo_Vetor V);
  int quantidade_vetor (Tipo_Vetor V);

Exemplo: inserção de dados no vetor
 int insere_vetor (V, Dado)
 Tipo_Vetor *V;
 Tipo_Dado Dado;
 {
   if (V->Fim < MAX_VETOR) /* Vetor nao esta cheio ? */
     {
       V->Dado[V->Fim]=Dado;
       V->Excluido[V->Fim]=FALSO;
      (V->Fim)++;
       return(OK);
      }
   else
     return(ERRO);
  }
                                    

Filas – FIFO = “First In, First Out”:


Construção de um conjunto de rotinas genéricas e modulares de manipulação de filas. Vamos aqui descrever uma possível proposta de um conjunto de rotinas empregadas para este fim.

Rotinas básicas de manipulação de FILAS usando vetores:
- Estruturas de dados com alocação estática
- Inserção no final da fila
- Remoção do início da fila
- Fila circular

Aplicação típica:
- Lista de elementos a espera de um tratamento

Estrutura de dados:
 typedef int Tipo_Dado;
 typedef struct {
                          Tipo_Dado Dado [MAX_FILA];
                          int Inicio, Fim;
                         } Tipo_Fila;

Rotinas:
 void inicializa_fila (Tipo_Fila *F);
 int insere_fila (Tipo_Fila *F; Tipo_Dado Dado);
 int retira_fila (Tipo_Fila *F; Tipo_Dado *Dado);
 void lista_fila (Tipo_Fila F);
 int consulta_fila (Tipo_Fila F; int Indice; Tipo_Dado *Dado);
 int cheia_fila (Tipo_Fila F);
 int vazia_fila (Tipo_Fila F);
 int quantidade_fila (Tipo_Fila F);
 int acha_fila (Tipo_Fila F; Tipo_Dado Dado; int *Indice);

Exemplo: inserção de dados na fila
 int insere_fila (F, Dado)
 Tipo_Fila *F;
 Tipo_Dado Dado;
 {
   int prox;
   prox=F->Fim+1;
   if (prox == MAX_FILA)
        prox=0;
   if (prox == F->Inicio)
       return(ERRO);
   else
        {
          F->Dado[F->Fim]=Dado;
          F->Fim=prox;
          return(OK);
        } 
  }

Pilhas – LIFO = “Last In, First Out”:


Construção de um conjunto de rotinas genéricas e modulares de manipulação de pilhas. Vamos aqui descrever uma possível proposta de um conjunto de rotinas empregadas para este fim.

Rotinas básicas de manipulação de PILHAS usando vetores:
- Estruturas de dados com alocação estática
- Inserção no topo da pilha
- Remoção do topo da pilha

Aplicação típica:
- Lista de “tarefas pendentes”, passagem de parâmetros nas linguagens de programação.

Estrutura de dados:
 typedef int Tipo_Dado;
 typedef struct {
                         Tipo_Dado Dado [MAX_PILHA];
                         int Base, Topo;
                        } Tipo_Pilha;
Rotinas:
 void inicializa_pilha (Tipo_Pilha *P);
 int insere_pilha (Tipo_Pilha *P; Tipo_Dado Dado);
 int retira_pilha (Tipo_Pilha *P; Tipo_Dado *Dado);
 void exibe_pilha (Tipo_Pilha P);
 int quantidade_pilha (Tipo_Pilha P);
 int cheia_pilha (Tipo_Pilha P);
 int vazia_pilha (Tipo_Pilha P);
 void esvazia_pilha (Tipo_Pilha *P);

Exemplo: inserção de dados na pilha
 int insere_vetor (P, Dado)
 Tipo_Vetor *P;
 Tipo_Dado Dado;
 {
   if (V->Fim < MAX_VETOR) 
     {
       P->Dado[P->Fim]=Dado;
       P->Excluido[P->Fim]=FALSO;
      (P->Fim)++;
       return(OK);
      }
   else
     return(ERRO);
  }

Deque – “Double Ended Queue”:


Construção de um conjunto de rotinas genéricas e modulares de manipulação de deques. Vamos aqui descrever uma possível proposta de um conjunto de rotinas empregadas para este fim.

Rotinas básicas de manipulação de DEQUES usando vetores:
- Estruturas de dados com alocação estática
- Inserção no início ou no final do deque
- Remoção do início ou do final do deque
- Estrutura de dados “circular”

Aplicação típica:
- Lista de elementos com múltiplas formas de considerar/manipular a ordem destes

Estrutura de dados:
 typedef int Tipo_Dado;
 typedef struct {
                         Tipo_Dado Dado [MAX_DEQUE];
                          int Inicio, Fim;
                        } Tipo_Deque;
Rotinas:
 void inicializa_deque (Tipo_Deque *D);
 int insere_inicio_deque (Tipo_Deque *D; Tipo_Dado Dado);
 int insere_final_deque (Tipo_Deque *D, Tipo_Dado Dado);
 int retira_inicio_deque (Tipo_Deque *D; Tipo_Dado *Dado);
 int retira_final_deque (Tipo_Deque *D; Tipo_Dado *Dado);
 void lista_deque (Tipo_Deque D);
 int acha_deque (Tipo_Deque D; Tipo_Dado Dado; int *Indice);
 int cheio_deque (Tipo_Deque D);
 int vazio_deque (Tipo_Deque D);
 int quantidade_deque (Tipo_Deque D);
 void apaga_deque (Tipo_Deque *D);


Caso queiram ter uma menor abstração e aprimorar os conceitos do conteúdo abordado neste post, recomendo os vídeos abaixo: 

Estrutura de dados - Lista estática



Estrutura de dados - Pilhas, Filas e Deque



"A mente que se abre a uma nova ideia jamais voltará ao seu tamanho original."
Albert Einstein