![]() |
![]() |
![]() |
Neste capítulo discutimos como se podem criar em C tipos de dados mais avançados e estruturados.
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 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;
![]() |
A definição de novos tipos com typedef pode também ser efectuada 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;
![]() |
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 respectivo. 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 activo 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
.
![]() |
O C é uma das poucas linguagens que permite a mudança de tipo das suas variáveis.
Para isso usa-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 fraccioná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 carácter '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 efectuar 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 */
![]() |
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.
No entando é possível, numa enumeração, associar os identicadores a outros valores, que não 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;
![]() |
É 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.
![]() |
1. Escreva um programa que usando tipos enumerados escreva a data do dia, na forma p. ex. 12 de Outubro de 1996.
2. Escreva um programa que construa uma base de dados simples relativa a pessoas. Deverá armazenar o nome, morada, data de nascimento, salário e categoria.
![]() |
2006
Antônio Paulo Neto