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 operacional UNIX, foi quase totalmente escrito em C.
& | 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 |
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 possuirem 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 à função:
int bitcount(unsigned char x)
{
int count;for (count = 0; x != 0; x>>=1)
if (x & 1)
count++;
return count;
}
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 {Aqui 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).
unsigned int fl1 : 1;
unsigned int fl2 : 1;
unsigned int fl3 : 1;
unsigned int type : 4;
unsigned int funny_int : 9;
} pack;
O acesso aos vários campos faz-se da forma habitual: o acesso ao campo type denota-se pack.type.
Notas: