Contents

Capítulo 7 - Outros Tipos de Dados


botões
botões Conteúdo Capítulo anterior Capítulo seguinte

Neste capítulo é apresentado como se pode criar em C tipos de dados mais avançados e estruturados.


Tópicos

setas
setas Up

Estruturas

As estruturas em C são muito semelhantes aos registos em Pascal. Agrupam num único tipo vários valores (campos), que podem ser de tipos diferentes.

Exemplo:

struct people 
  {
  char name[50];
  int age;
  float salary;
  };
struct people John;

As declarações anteriores definem uma estrutura chamada people e uma variável (John) desse mesmo tipo.

Note-se que a estrutura definida tem um nome (tag) que é opcional.

Quando as estruturas são definas com tag podemos declarar posteriormente variáveis, argumentos de funções e, também, tipos de retorno, usando esse tag, como se mostrou no exemplo anterior.

Também é possível definir estruturas sem tag (anónimas). Neste último caso as variáveis terão de ser nomeadas entre o último } e ; da forma habitual.

Por exemplo:

struct { 
     char name[50]; 
     int age;
     float salary;
     } John;

Podemos ainda inicializar as variáveis do tipo estrutura quando da sua declaração, colocando os valores pela ordem dos campos definidos na estrutura entre chavetas:

struct people John = { "John Smith", 26, 124.5 };

Para aceder um campo ou membro, de uma estrutura, utiliza-se, à semelhança do Pascal, o operador . . Para aumentar o salário do Sr. John Smith deveria escrever-se:

John.salary = 150.0;
setas
setas Up

Uso de typedef

A definição de novos tipos com typedef pode também ser efetuada com estruturas. A declaração seguinte define um novo tipo person, podendo posteriormente declarar-se e inicializar-se variáveis desse tipo:

typedef struct people
  {
    char name[50];
    int age;
    float salary;
  }
  person;
  
person John = { "John Smith", 26, 124.5 };

Neste exemplo o nome people também funciona como tag da estrutura e também é opcional. Nesta situação a sua utilidade é reduzida.

Arrays e estruturas podem ser misturados (aliás como quaisquer outros tipos):

  typedef struct people
  {
  char name[50];
  int age;
  float salary;
  } person;
  
  person team[1000];

Aqui a variável team é composta por 1000 person's.

Um acesso poderia ser, por exemplo:

team[41].salary = 150.0;

ou,

total_salaries += team[501].salary;
setas
setas Up

Uniões

Uma variável do tipo união pode conter (em instantes diferentes) valores de diferentes tipos e tamanhos. Na linguagem C a declaração de uniões é em tudo semelhante à declaração de estruturas, empregando-se a palavra union em vez de struct.

Por exemplo, podemos ter:

  union number {
  short sort_number;
  long long_number;
  double real_number;
  } a_number;

Aqui declara-se uma união chamada number e uma variável desse tipo - a_number. Esta variável poderá conter, em instantes diferentes, um short, um long ou um double. A distinção faz-se pelo nome do campo respetivo.

Quando se preenche um determinado campo, o que estiver noutro qualquer é destruído. Os vários campos têm todos o mesmo endereço na memória.

Os campos ou membros são acedidos da mesma forma do que nas estruturas:

printf("%ld\n", a_number.long_number);

Quando o compilador reserva memória para armazenar uma união, apenas reserva o espaço suficiente para o membro maior (no exemplo de cima serão 8 bytes para o double). Os outros membros ficam sobrepostos, com o mesmo endereço inicial.

Uma forma comum de num programa se tomar nota do membro ativo em cada instante é embeber a união dentro de uma estrutura, onde se acrescenta um outro membro com essa função. Examinar atentamente o exemplo que se segue:

typedef struct
  {
    int max_passengers;
  } jet;

  typedef struct
  {
    int lift_capacity;
  } helicopter;

  typedef struct
  {
    int max_payload;
  } cargo_plane;

  typedef union
  {
    jet jet_unit;
    helicopter heli_unit;
    cargo_plane cargo_unit;
  } aircraft;

  typedef struct
  {
    aircraft_type kind;
    int speed;
    aircraft description;
  } an_aircraft;

No tipo an_aircraft inclui-se um membro description que é do tipo aircraft que é, por sua vez uma união de 3 estruturas diferentes (jet, helicopter e cargo_plane).

Inclui-se ainda no tipo an_aircraft um membro (kind) que indica qual é o membro válido no momento para a união description.

setas
setas Up

Conversão entre tipos (type casting)

O C é uma das linguagens que permite a mudança de tipo das suas variáveis. Para isso utiliza-se o operador (cast), onde cast é o novo tipo pretendido. Assim é possível escrever:

  int k;
  float r = 9.87;
  k = (int) r;

A variável k ficará com o valor 9, sendo a parte fracionária de r descartada.

O casting pode ser usado com qualquer um dos tipos simples, e muitas vezes quando é omitido o compilador executa a conversão silenciosamente. Outro exemplo:

  int k;
  char ch = 'A';
  k = (int) ch;

Aqui o valor de k será 65 (o código ASCII do caractere 'A').

Outro uso frequente do casting é assegurar que a divisão entre inteiros não seja uma divisão inteira. Basta para isso converter para real o numerador ou denominador. O outro operando é assim também automaticamente convertido, antes de se efetuar a operação:

int j = 2, k = 5;
  float r;
  
  r = (float) j/k;  /* r = 0.4 em vez de 0, se não se fizesse o cast de j */
setas
setas Up

Tipos enumerados

Os tipos enumerados não são mais do que uma lista de identificadores que funcionam como constantes inteiras. São muito semelhantes aos tipos enumerados do Pascal.

Por exemplo:

  enum days { sun, mon, tue, wed, thu, fri, sat };
  enum days week1, week2;
  week1 = wed;
  week2 = sun;

Na declaração de cima o identificador sun fica associado ao valor 0, o identificador mon ao valor 1, e assim sucessivamente. É possível fazer cast (implícito ou explícito) para int, nos dois sentidos.

É possível, numa enumeração, associar os identificadores a outros valores, que não seja a sucessão que começa em 0.

Por exemplo:

enum escapes {bell='\a', backspace='\b', tab='\t',
  newline='\n', vertical_tab='\v', return='\r'};

É mesmo possível iniciar a sucessão de valores inteiros associados aos identificadores num valor diferente de 0, como na declaração seguinte:

enum months {jan=1, feb, mar, apr, may, jun, jul, ago, sep, oct, nov, dec};

No exemplo apresentado para as uniões, atrás, aparecia um tipo aircraft_type. Esse tipo pode ser definido como uma enumeração:

typedef enum {a_jet, a_helicopter, a_cargo_plane} aircraft_type;
setas
setas Up

Variáveis estáticas

É possível, quando da declaração de variáveis, locais ou não, prefixá-las com o qualificador static.

As variáveis locais estáticas são inicializadas uma só vez e não desaparecem quando a função a que pertencem termina, podendo no entanto ser acedidas apenas dentro da função. As variáveis globais estáticas apenas podem ser acedidas no ficheiro onde são declaradas, não sendo exportadas para o exterior.

As variáveis locais estáticas também mantêm o seu valor de umas chamadas para as outras, como se vê no exemplo seguinte:

  #include <stdio.h>

  void stat(void);  /* Protótipo de stat() */

  void main(void)
  {
    int k;

    for (k=0; k<5; k++)
    stat();
  }

  void stat(void)
  {
    int var = 0;
    satic int st_var = 0;

    printf("var = %d, auto_var = %d\n", var++, auto_var++);
  }

A saída deste programa será:

  var = 0, auto_var = 0
  var = 0, auto_var = 1
  var = 0, auto_var = 2
  var = 0, auto_var = 3
  var = 0, auto_var = 4

A variável var é criada e inicializada de cada vez que a função é chamada. A variável auto_var só é inicializada da primeira vez e lembra-se do valor que tinha na última vez que a função terminou.

setas
setas Up

Exercícios

setas
setas Up