![]() |
![]() |
![]() |
Neste capítulo é apresentado como se pode 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 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;
![]() |
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;
![]() |
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
.
![]() |
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 */
![]() |
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;
![]() |
É 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.
![]() |
![]() |