Laboratório de Programação
Prof. Oscar Luiz Monteiro de Farias
[email protected]
Capítulo 5
Estruturas
Estruturas (1)





Uma estrutura (registros, em outras LP's) é uma coleção de
uma ou mais variáveis, possivelmente de diferentes tipos.
Estas variáveis são colocadas juntas, sob um único nome, para
manipulação conveniente.
Permitem que um grupo de variáveis relacionadas sejam
tratadas como uma unidade.
Ex.: registro relativo a um aluno em um sistema de
administração acadêmica (#matrícula, nome, sexo, curso, etc.)
Aplicações gráficas: ponto (par de coordenadas), retângulo
(par de pontos)...
Estruturas (2)





Estruturas podem ser copiadas e atribuídas, passadas como
argumentos para funções e retornadas por funções.
Estruturas e vetores automáticos podem ser inicializados.
A palavra-chave (keyword) struct introduz uma declaração
de estrutura, que é uma lista de declarações entre chaves.
Um nome opcional, chamado etiqueta de estrutura
(structure tag), pode se seguir à palavra struct, e pode ser
usada posteriormente como uma abreviação para a parte da
declaração entre chaves.
As variáveis nomeadas em uma estrutura são denominadas
membros (members).
Estruturas (3)


Exemplos de estruturas em aplicações gráficas
O objeto básico é um ponto, com coordenadas x e
y.
struct point {
int x;
int y;
};
Estruturas (4)





Um membro ou uma etiqueta (tag) de uma estrutura podem ter
o mesmo nome que uma variável comum, pois sempre poderão
ser distingüidas pelo contexto.
Os mesmos nomes de membros podem ocorrer em diferentes
estruturas, sem conflitos.
Uma declaração struct define um tipo.
O fecha-chave ''}'' que termina a lista de membros pode ser
seguido por uma lista de variáveis, de modo similar a qualquer
tipo básico.
struct { ... } x, y, z;
É sintaticamente análogo a:
int x, y, z;
Estruturas (5)
Uma declaração de estrutura que não é seguida por
uma lista de variáveis não reserva qualquer área de
memória. Simplesmente descreve um template ou
modelo de uma estrutura.
 Se a declaração da estrutura tiver uma tag, esta
poderá posteriormente ser usada em definições de
instâncias da estrutura.
struct point pt;
Define uma variável pt que é uma struct do tipo
point.

Estruturas (6)



Pode-se inicializar uma estrutura seguindo-se a sua
definição por uma lista de inicializadores para os
membros, cada um sendo uma expressão constante.
struct maxpt = { 320, 200 };
Uma estrutura automática pode também ser inicializada
por atribuição ou invocando-se uma função que retorne
uma estrutura do tipo desejado.
Em uma expressão referencia-se um membro de uma dada
estrutura por meio de uma construção do tipo:
structure-name.member
Estruturas (7)


O operador de membro de estrutura ''.'' conecta o
nome de uma variável do tipo estrutura e o nome
do membro.
Para se imprimir as coordenadas do ponto pt, p.ex.:
printf("%d,%d", pt.x, pt.y);
Para cálculo da distância do ponto (0,0) a pt:
double dist, sqrt(double);
dist = sqrt((double)pt.x * pt.x + (double)pt.y * pt.y);
Estruturas (8)

Estruturas podem ser edentadas.

Ex.: um retângulo é um par de pontos, denotando vértices opostos.
struct rect {
struct point pt1;
struct point pt2;
};

Se declararmos: struct rect screen;
então screen.pt1.x
irá referir-se à coordenada x do membro pt1 de screen.
Estruturas e funções (1)





As operações legais em estruturas são a ''cópia'' e
''atribuição'' (como uma unidade), tomando-se seu
endereço com o operador ''&'' e fazendo acesso a seus
membros.
''cópia'' e ''atribuição'' incluem a passagem de argumentos
para funções e o retorno de valores de funções.
Estruturas não podem ser comparadas.
Uma estrutura pode ser inicializada por uma lista de
valores constantes correspondendo a seus membros.
Uma estrutura automática pode ser inicializada por uma
atribuição.
Estruturas e funções (2)

Funções, envolvendo estruturas, para manipular pontos e retângulos:
/* makepoint: make a point from x and y components */
struct point makepoint(int x, int y)
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}

Não há conflitos entre os nomes de argumento e membros com
mesmo nome.
Estruturas e funções (3)

makepoint pode ser usada para inicializar estruturas
dinâmicamente, ou para fornecer argumentos de
estrutura para uma função:
Estruturas e funções (4)



Funções para realizar aritmética com pontos.
Os argumentos e o valor retornado são estruturas.
Os parâmetros da função, do tipo struct, são
passados por valor, como quaisquer outros.
Estruturas e funções (5)



A função ptinrect testa se um ponto se encontra
''dentro'' de um retângulo.
Convenção: um retângulo inclui sua área interna e
mais seus lados esquerdo e inferior.
Um retângulo está representado pelos seus vértices
inferior esquerdo e superior direito.
Estruturas e funções (6)

A função a seguir retorna um retângulo
necessariamente em forma canônica:
Estruturas e funções (7)





Se uma estrutura maior tiver que ser passada para
uma função, em geral é mais eficiente se passar um
pointer do que copiar a estrutura inteira.
Os pointers para estruturas são similares aos
pointers para variáveis comuns.
struct point *pp;
diz que pp é um pointer para um tipo struct point.
pp é a estrutura
(*pp).x e (*pp).y são seus membros.
Estruturas e funções (8)



Para se usar pp, poder-se-ia, p. ex., escrever:
struct point origin, *pp;
pp = &origin;
printf("origin is (%d,%d)\n", (*pp).x, (*pp).y);
Os parênteses são necessários em (*pp).x porque a
precedência do operador de membros de estrutura
''.'' é maior do que a de ''*''.
A expressão *pp.x significaria *(pp.x), que seria
ilegal, pois x não é um pointer.
Estruturas e funções (9)




Os apontadores para estruturas são usados com tanta
freqüência que existe uma notação alternativa, abreviada.
Se p é um pointer para uma estrutura, então
p->member-of-structure
refere-se ao membro em particular.
printf("origin is (%d,%d)\n", pp->x, pp->y);
''.'' e ''->'' associam da esquerda para a direita. Se tivermos
struct rect r, *rp = &r;
Serão equivalentes as expressões:
r.pt1.x / rp->pt1.x/ (r.pt1).x / (rp->pt1).x
Estruturas e funções (10)


O operador de estruturas . e ->, juntamente com () para
invocação de funções e [] para subscritos, estão no topo da
hierarquia de precedência e, assim prendem fortemente.
Dada a declaração
struct {
int len;
char *str;
} *p;
então ++p->len incrementa len, e não p, pois a
parentetisação implícita é ++(p->len).
Estruturas e funções (11)






(++p)->len incrementa p antes do acesso a len.
(p++)->len incrementa p após o acesso (Neste
caso os parentêses são redundantes).
*p->str obtém qualquer objeto apontado por str.
*p->str++ incrementa str após ter acesso ao que
ele (str) aponta (tal como *s++).
(*p->str)++ incrementa aquilo para o qual str
aponta.
*p++->str incrementa p após ter acesso ao que str
aponta.
Vetores de Estruturas (1)




Considere um programa para contar as ocorrências
de cada palavra-chave em C.
Será necessário um vetor de cadeias de caracteres
para armazenar as palavras-chaves e um outro vetor
de inteiros para armazenar os contadores.
Pode-se usar dois vetores independentes e
paralelos:
char *keyword[NKEYS];
int keycount[NKEYS];
Ou ainda, um vetor de estruturas.
Vetores de Estruturas (2)




struct key {
char *word;
int count;
} keytab[NKEYS];
Declara uma estrutura do tipo key, define um array keytab
de estruturas desse tipo, e reserva área de armazenamento
para eles.
Cada elemento do array é uma estrutura.
Cada elemento do array é um par (por palavra-chave):
char *word;
int count;
Vetores de Estruturas (3)



Alternativamente poder-se-ia ter:
struct key {
char *word;
int count;
};
struct key keytab[NKEYS];
Como a estrutura keytab contém um conjunto constante de
nomes é conveniente torná-la uma variável externa e
inicializá-la, quando de sua definição.
A definição é seguida por uma lista de inicializadores
entre chaves.
Vetores de Estruturas (4)

Inicialização de vetores de estruturas:
Vetores de Estruturas (5)
Programa de contagem de palavras-chaves (keywords) em
um programa escrito em C.
ver prog61-chap06-pg110.c
i) O programa tem início com a definição da estrutura
keytab. A lista de keywords deve estar ordenada em ordem
crescente em keytab.
ii) main lê a entrada repetidamente invocando a função
getword, que obtém da entrada, uma palavra de cada vez.
iii) cada palavra é pesquisada em keytab por uma versão da
pesquisa binária binsearch.

Vetores de Estruturas (6)
Cada chamada a getword encontra uma palavra, que é
copiada para um vetor (cadeia de caracteres) indicada pelo
seu primeiro argumento.
 O tamanho do array keytab é determinado em tempo de
compilação.
 O número de elementos em keytab é dado por:
(tamanho de keytab) / (tamanho da struct key)
 Ou, usando-se o operador size of:
define NKEYS (sizeof keytab / sizeof(struct key)) ou
#define NKEYS (sizeof keytab / sizeof(keytab[0]))

O operador sizeof




sizeof objeto ou
sizeof (nome do tipo)
geram um inteiro igual ao tamanho, em bytes, do
objeto ou tipo especificado.
sizeof produz um valor inteiro não sinalizado,cujo
tipo size_t é definido no header <stdef.h>.
Um sizeof não pode ser usado em uma linha #if,
pois o pré-processador não analisa nomes de tipo.
Mas a expressão no #define não é avaliada pelo
pré-processador e o código sugerido é válido.
Pointers para estruturas (1)






Para ilustrar alguns conceitos eis uma nova versão do programa de
contagem de palavras-chaves envolvendo pointers ao invés de
índices de arrays.
ver prog62-chap06-pg112.c
A declaração de binsearch deve indicar o retorno de um pointer para
struct key, ao invés de um inteiro. Isto afeta o protótipo da função e a
própria função binsearch.
Se binsearch encontra a palavra, ela retorna um pointer para o
elemento do array de estruturas que a contém; senão, retorna NULL.
Agora, tem-se acesso aos elementos de keytab via pointers.
O mais importante é ajustar o algoritmo para que não gere pointers
ilegais ou tente efetuar um acesso for a dos limites do array.
Pointers para estruturas (2)



Em main tem-se:
for (p = keytab; p < keytab + NKEYS; p++)
Se p é um pointer para uma estrutura, aritmética de
ponteiros em p leva em consideração o tamanho da
estrutura, assim p++ incrementa p para apontar
para o próximo elemento do array de estruturas.
Não se deve assumir, todavia, que o tamanho de
uma estrutura é a soma do tamanho de seus
membros.
Pode haver necessidade de ''alinhamentos''.
Pointers para estruturas (3)
struct {
char c;
int i;
};
se um char ocupar 1 byte e um int, 4 bytes, o
tamanho da estrutura acima poderá exigir 8 bytes.
 O operador sizeof sempre retornará o valor
apropriado.
 Convenção para editar os programas que possuem
funções que retornam pointers para estruturas,
Estruturas auto-referenciadas (1)




Problema: contar as ocorrências de todas as palavras que
aparecem em um fluxo de entrada.
ver prog63-chap06-pg115.c
Como a lista de palavras não é conhecida a priori, não se
pode ordená-la e usar a pesquisa binária.
Não se pode fazer uma pesquisa linear para cada palavra,
assim que a mesma estiver disponível, pois o seu tempo de
execução poderia crescer exponencialmente.
Uma solução é manter o conjunto de palavras vistas até o
momento ordenadas, colocando-as na posição correta
assim que estiverem disponíveis.
Estruturas auto-referenciadas (2)



Esta solução não pode ser aplicada a um vetor linear, pois
demandaria muito tempo.
Alternativamente será usada uma estrutura de dados denominada
árvore binária.
“now is the time for all good men to come to the
aid of their party”
Estruturas auto-referenciadas (3)
A árvore contém um nodo para cada palavra distinta.
 Cada nodo contém:
- um pointer para o texto da palavra
- um contador do número de ocorrências
- um pointer para o nodo filho esquerdo
- um pointer para o nodo filho direito
 Nenhum nodo pode ter mais de dois filhos.
 A organização da árvore é tal que, para qualquer nodo, a
subárvore esquerda contenha apenas palavras
lexicograficamente menores que a palavra no nodo, e, a
subárvore à direita, palavras maiores.

Estruturas auto-referenciadas (4)
Aplica-se um algoritmo recursivo para se verificar
se a palavra procurada já está na árvore.
i) Inicia na raiz e compara a palavra com o texto
armazenado neste nodo.
ii) Se for igual, então achou.
iii) Se a palavra for menor lexicograficamente que
o texto, procura na subárvore da esquerda.
iv) Se não procura na subárvore da direita.
Se não houver filho no sentido especificado (D|E),
a nova palavra não está na árvore.

Estruturas auto-referenciadas (5)


Um nodo será representado por:
Esta é uma declaração recursiva.
struct tnode *left;
declara left sendo um pointer para tnode, e não sendo ele
mesmo um tnode, o que não seria admissível.
Estruturas auto-referenciadas (6)



Estruturas que se referem umas às outras:
A função main lê as palavras com getword e as
instala na árvore com addtree.
A função addtree é recursiva.
Estruturas auto-referenciadas (7)




Eventualmente a palavra casa com algum texto já armazenado
em algum nodo na árvore (incrementa-se o contador), ou um
null pointer é encontrado, indicando que um novo nodo deve
ser criado (para armazenar a palavra) e adicionado à árvore.
Nesse último caso, addtree returna um pointer para ele, o qual
é instalado no nodo pai.
A memória para um novo nodo é obtida por talloc, que retorna
um pointer para um espaço livre capaz de armazenar um nodo
da árvore, e a nova palavra é copiada para lá por strdup.
O contador é inicializado, e os dois filhos (E|D) são feitos null.
Esta parte do código é executada apenas nas folhas da árvore,
quando um novo nodo está sendo adicionado.
Estruturas auto-referenciadas (8)






treeprint imprime a árvore ordenada; em cada nodo, ela
imprime a subárvore da esquerda (<s ) , então a própria
palavra, então a sub-árvore da direita (>s).
Se a árvore se tornar desbalanceada, o tempo de execução
do programa pode crescer rapidamente.
Pior caso: se as palavras já estiverem ordenadas.
Há generalizações de árvore binária que solucionam esse
problema...
A função malloc assegura o alinhamento da struct.
É declarada no arquivo header <stdlib.h>
Pesquisa em tabela (1)





Problema: package para pesquisa em tabelas, ilustrando o
uso de estruturas e pointers.
ver prog64-chap06-pg118.c
Problema típico do gerenciamento de uma tabela de
símbolos.
#define IN
1
com esta definição, o nome IN e o texto substituto 1 são
armazenados em uma tabela.
Mais além, quando IN aparecer em um comando do tipo
state = IN;
deverá ser substutuído por 1.
Pesquisa em tabela (2)


Há duas funções para manipular os nomes e textos
substitutos:
install(s,t)
registra o nome s e o texto substituto t em uma tabela; s e t
são cadeias de caracteres..
lookup(s) procura por s na tabela, e retorna um pointer
para o local onde foi encontrado, ou NULL se s não se
encontrava na tabela..
Usa-se um algoritmo hash e uma hashtable para inclusão
do par nome/texto substituto na tabela, se o mesmo ainda
não existir, ou para a sua substituição, em caso contrário.
Pesquisa em tabela (3)


No algoritmo da pesquisa, o nome de entrada é convertido (via a função
hash) em um short int não negativo, que é então usado como um índice
para um array de pointers.
Um elemento do array aponta para o início de uma lista encadeada de
blocos descrevendo os nomes que possuem aquele valor hash. Ele tem o
valor NULL se nenhum nome possui aquele valor hash.
Pesquisa em tabela (4)

Um bloco na lista é uma estrutura contendo
pointers para o nome, o texto substituto, e o
próximo bloco na lista. Um pointer next igual a
NULL sinaliza o fim da lista.
Pesquisa em tabela (5)

O vetor de pointers resume-se a:
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */



A função hash, que é usada por lookup e install, gera um
número n, tal que 0 <= n < HASHSIZE, para ser usado
como índice em hashtab, o array de pointers.
Não é a melhor função hash, porém é simples e eficiente.
O tipo de retorno não sinalizado assegura que os valores
gerados por hash serão não-negativos.
Pesquisa em tabela (6)




O processo de hash/pesquisa (lookup) produz um índice
inicial no array hashtab; Se a cadeia já existe, ela estará na
lista de blocos iniciando-se nesse índice.
A pesquisa é realizada pela função lookup.
Se lookup identifica que a cadeia já está presente ela
retorna um pointer para a mesma; senão ela retorna NULL.
install usa lookup para determinar se o nome sendo
instalado já está presente; se estiver, a nova definição
substituirá a antiga. Senão, uma nova entrada será criada.
Install retorna NULL se, por qualquer razão, não há mais
espaço para uma nova entrada.
Typedef (1)


C provê uma facilidade – typedef – para criar novos
nomes para tipos de dados.
typedef int Length;
faz que o nome Length seja um sinônimo para int.
O tipo Length pode ser usado em declarações,
casts, etc., exatamente da mesma forma que o tipo
int.
Length len, maxlen;
Length *lengths[];
Typedef (2)



typedef char *String;
torna String um sinônimo para char * ou pointer para uma
cadeia de caracteres, podendo ser usado em declarações e
casts:
String p, lineptr[MAXLINES], alloc(int);
int strcmp(String, String);
p = (String) malloc(100);
Sintaticamente, typedef é como as classes de
armazenamento extern, static, etc.
Inicia-se o nome dos typedef's com letras maiúsculas, a fim
de destacá-los.
Typedef (3)


Uso de typedef's para a definição dos nodos de uma árvore (
vide definição da árvore em prog63-chap06-pg115.c)
typedef struct tnode *Treeptr;
typedef struct tnode { /* the tree node: */
char *word;
/* points to the text */
int count;
/* number of occurrences */
Treeptr left;
/* left child */
Treeptr right;
/* right child */
} Treenode;

Esta declaração cria dois novas keywords para tipos: Treenode
(uma estrutura) e Treeptr (um pointer para a estrutura).
Typedef (4)


A função talloc tornar-se-ia:
Treeptr talloc(void)
{
return (Treeptr) malloc(sizeof(Treenode));
}
Deve-se enfatizar que uma declaração typedef não
cria um novo tipo em qualquer sentido; ela
simplesmente fornece um novo nome para um tipo
existente.
Typedef (5)



typedef não adiciona nova semântica ao C.
As variáveis declaradas via typedef têm exatamente as
mesmas propriedades das variáveis declaradas
explicitamente.
typedef é como #define, exceto que, pelo fato de ser
interpretada pelo compilador, ela pode lidar com
substituições textuais que vão além das capacidades do
pré-processador. Por exemplo:
typedef int (*PFI)(char *, char *);
cria o tipo PFI, significando pointer para função de dois
argumentos char * e que retorna um int.
Typedef (6)


O tipo PFI poder ser usado em contextos similares ao do
programa de ordenação prog58-chap05-pg98.c
PFI strcmp, numcmp; /* ao invés de */
int (*comp)(void *, void *)
Há dois motivos principais para se usar typedef's:
i) parametrizar um programa para resolver problemas de
portabilidade. Se typedef's forem usados para tipos de
dados dependentes da máquina, apenas os typedef's
precisarão ser alterados.
ii) melhorar a documentação do programa.
Unions (1)



Uma union é uma variável que pode conter (em
momentos diferentes) objetos de diferentes tipos e
tamanhos, com o compilador mantendo o controle
dos requisitos de tamanho e alinhamento.
Unions fornecem um meio para manipular
diferentes tipos de dados em uma única área da
memória, sem a necessidade de acrescentar
qualquer informação dependente de máquina ao
programa.
São semelhantes aos variant records do Pascal.
Unions (2)




Exemplo: Um problema típico de gerenciamento da
tabela de símbolos de um compilador.
Supor que uma constante possa ser do tipo int, float
ou um pointer para uma cadeia de caracteres.
O valor da constante deve ser armazenado em uma
variável do tipo apropriado, por outro lado, facilita
o gerenciamento da tabela que o valor ocupe a
mesma quantidade de memória e que seja guardado
no mesmo lugar, independente do seu tipo.
Esta é a finalidade da union.
Unions (3)




A sintaxe da union é baseada em structs.
union u_tag {
int ival;
float fval;
char *sval;
} u;
A variável u terá tamanho suficiente para conter o maior
dos três tipos.
O tamanho específico é dependente da implementação.
Unions (4)




Qualquer um desses tipos pode ser atribuído a u e depois
usado em expressões, desde que o uso seja consistente: o
tipo recuperado deve ser o tipo mais recentemente
armazenado.
A responsabilidade é do programador.
O resultado depende da implementação, caso algo seja
armazenado como um tipo e extraído como outro.
Sintaticamente tem-se acesso aos membros de uma union,
de forma similar aos membros de uma struct.
union-name.member
union-pointer->member
Unions (5)

Se a variável utype é usada para controlar o tipo
correntemente armazenado em u, então em um
trecho de um programa poder-se-ia encontrar:
Unions (6)


As unions podem ocorrer dentro de structs e arrays e viceversa.
A notação para se ter acesso a membros de uma union em
uma struct (ou vice-versa) é idêntica àquela das estruturas
aninhadas.
Unions (7)




Referencia-se o membro ival da forma:
symtab[i].u.ival
Referencia-se o 10 caracter da cadeia sval por:
symtab[i].u.sval /* ou alternativamente por */
symtab[i].u.sval[0]
Ou seja, uma union é uma struct em que todos os membros
possuem deslocamento zero a partir da base.
A struct é grande o suficiente para conter o maior membro
e o alinhamento é adequado a todos os tipos de union.
Unions (8)
As mesmas operações são permitidas em unions e
structs: atribuição ou cópia como unidade, tomada
de endereço, e acesso a membros.
 Uma union só pode ser inicializada com um valor
compatível com o tipo de seu primeiro membro
(no exemplo anterior, um int).
 O alocador de memória do capítulo 8 mostra como
uma union pode ser usada para forçar uma variável
a ser alinhada com a fronteira de uma área de
armazenamento específica.

Campos de bits (1)




Em situações de escassez de memória pode ser necessário
empacotar diversos objetos em uma única palavra de máquina.
Um uso comum seria um conjunto de bits flags em aplicações
como tabelas de símbolos em compiladores e formatos de
dados impostos externamente, como interfaces para
dispositivos de hardware.
Imagine um fragmento do código de um compilador que
manipula uma tabela de símbolos. Cada identificador em um
programa possui certas informações associadas com ele, do
tipo se é ou não uma keyword, external, static, etc.
A forma mais compacta de codificar tais informações é um
conjunto de flags de 1bit acondicionadas em um char ou int.
Campos de bits (2)

O usual é definir-se um conjunto de máscaras
correspondentes às posições dos bits relevantes.
#define KEYWORD 01
#define EXTERNAL 02
#define STATIC
04
/* alternativamente */
enum {KEYWORD = 01, EXTERNAL = 02, STATIC = 04};


Os números devem ser potência de 2.
Para se ter acesso à informação precisa-se apenas
manusear os bits com operações de deslocamento,
complemento e máscara.
Campos de bits (3)




flags |= EXTERNAL | STATIC;
ativaria os bits EXTERNAL e STATIC em flags,
enquanto
flags &= ~(EXTERNAL | STATIC);
os desativaria.
if ((flags & (EXTERNAL | STATIC)) == 0) ...
seria verdadeiro se ambos os bits estivessem off.
C oferece a possibilidade de se definir e ter acesso
a campos diretamente dentro de uma palavra, ao
invés de se usar os operadores lógicos bit a bit.
Campos de bits (4)

Um campo de bits (bit-field) é um conjunto adjacentes de bits dentro
de uma única unidade de armazenamento definida pela
implementação (palavra ou word).

A sintaxe de definição e acesso aos campos é baseada em estruturas.

A tabela de símbolos #define (slide 61) poderia ser substituída por:
struct {
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
} flags;
que define uma variável flag com três campos de 1 bit.

O número após '':'' representam a largura do campo em bits.
Campos de bits (5)




Os campos são declarados como unsigned int para se ter certeza
de serem quantidades não sinalizadas.
Os campos individuais são referenciados da mesma forma que
outros membros de estrutura: flags.is_keyword, flags.is_extern,
etc.
Os campos comportam-se como short int's e podem participar
de expressões aritméticas, assim como outros inteiros.
Os exemplos anteriores poderiam ser escritos como:
flags.is_extern = flags.is_static = 1; /* para ativar os bits */
flags.is_extern = flags.is_static = 0; /* para desativá-los */

if (flags.is_extern == 0 && flags.is_static == 0) /* para testálos */
Campos de bits (6)








Quase tudo a respeito de campos de bits depende da implementação,
e.g., se um bit field pode ultrapassar o limite de uma palavra.
Os bit fields não precisam ser nomeados.
Campos não nomeados (só '':'' e o tamanho) são usados para
preenchimento.
O tamanho especial 0 pode ser usado para forçar o alinhamento no
próximo limite de palavra.
Campos são atribuídos da direita para a esquerda ou da esquerda
para a direita (depende da máquina).
Os campos de bits só podem ser declarados como ints.
Especifique signed ou unsigned explicitamente, por questões de
portabilidade.
O operador & não pode ser aplicado aos campos de bits.
Download

struct