![]() |
![]() |
![]() |
Vimos em capítulo anterior como os apontadores permitem operar sobre a memória a baixo nível. De igual modo, o C possui outros operadores que atuam ao nível do byte e do bit.
Estes operadores podem ser muito úteis em certos programas que necessitam de controlar de perto o hardware, como por exemplo, na programação de sistema (ao nível do sistema operativo).
A combinação dos apontadores e operadores a nível do bit, faz do C uma linguagem capaz de substituir o assembly nas aplicações que dele necessitavam. Como já se referiu, o sistema operativo UNIX, foi quase totalmente escrito em C.
![]() |
Os operadores bit a bit do C estão sumarizados na tabela seguinte:
operador | significado |
---|---|
& | AND bit a bit |
| | OR bit a bit |
^ | XOR bit a bit |
~ | NOT (inversão) bit a bit |
<< | deslocamento (shift) para a esquerda |
>> | deslocamento (shift) para a direita |
É necessário conhecer a diferença entre o
operador &
(AND bit a
bit) e o operador &&
(
AND lógico entre duas condições cujo resultado é um dos dois valores
booleanos true ou false). O mesmo acontece com os
operadores |
e ||
.
O operador ~
é um operador
unário, i. e., opera apenas sobre um operando que terá de ser colocado à sua
direita.
Os operadores de shift executam o deslocamento do operando colocado à sua esquerda, um número de posições indicado pelo operando da direita.
Este último operando terá obrigatoriamente de ser um inteiro positivo.
No deslocamento para a
esquerda (<<
) as posições
que ficam livres são ocupadas com bits em 0. No deslocamento para a
direita (>>
) as posições livres
são ocupadas com bits em 0, se a quantidade deslocada for sem sinal
(unsigned), ou com bits idênticos ao mais significativo, se as
quantidades deslocadas possuírem sinal (signed).
Por exemplo, x << 2
,
desloca a representação binária do valor contido
em x
, duas posições (bits) para a
esquerda.
Se x
contiver o valor binário
00000010 (2 em decimal)
então: x << 2
faz com
que x
passe a conter o valor
00001000 (8 em decimal),
e x >> 2
faz com
que x
passe a conter o valor 00000000
(0 em decimal).
Assim, cada posição deslocada para a esquerda corresponde a uma multiplicação por 2, e cada posição deslocada para a direita corresponde a uma divisão por 2. Geralmente as operações de shift são bastante mais rápidas que as correspondentes operações de multiplicação e divisão.
Para ilustrar alguns destes operadores apresenta-se a seguinte função, capaz de contar o número de bits em 1 contidos num valor de 8 bits (unsigned char), passado como argumento na função:
int bitcount(unsigned char x)
{
int count;
for (count = 0; x != 0; x>>=1)
if (x & 1)
count++;
return count;
}
![]() |
Os campos de bits (bitfields), permitem o empacotamento de vários valores numa palavra de memória (16 ou 32 bits). A aplicação principal é poder trabalhar com valores distintos empacotados num registo de hardware (p. ex. posições de I/O de controlo de alguns periféricos).
Poderá também servir para a leitura de ficheiros contendo representações não standard (p. ex. inteiros de 9 bits).
Este empacotamento é feito em C através da declaração de uma estrutura, onde a seguir à definição de cada campo se acrescenta o tamanho, em bits, que o mesmo ocupa numa palavra de memória.
Por exemplo:
struct packed_struct
{
unsigned int fl1 : 1;
unsigned int fl2 : 1;
unsigned int fl3 : 1;
unsigned int type : 4;
unsigned int funny_int : 9;
} pack;
Neste exemplo a
variável pack
ocupa um total de
16 bits (1 palavra) e contém 5 campos: 3 flags de 1 bit, um
campo type
de 4 bits (podendo tomar
valores de 0 a 15), e um inteiro não standard de 9 bits (valores de 0 a
511).
O acesso aos vários campos faz-se da forma habitual: o acesso ao
campo type
denota-se pack.type
.
Notas:
![]() |
![]() |