A linguagem C é conhecida mundialmente até os dias atuais, utilizada para softwares que necessitam de funcionalidades mais baixas ou desenvolvimento de sistemas operacionais. A linguagem foi desenvolvida nos anos de 1970 por um dos criadores do Unix e utilizada para modernizar o próprio sistema operacional que anteriormente era escrito em assembly. Posteriormente a linguagem serviu de base para a criação da linguagem C++.
A linguagem C++ ou CPP é reconhecida por oferecer diversas possibilidades de desenvolvimento, podendo utilizar paradigmas de programação distintos, como o estrutural e o orientado a objetos conforme a sua evolução, contudo ela carrega em seu núcleo a base da linguagem C dando possibilidades de trabalhar em um nível mais baixo de desenvolvimento como operadores lógicos bitwise.
Bit a bit
Os operadores lógicos bitwise significam bit a bit, sendo possível a aplicação de portas lógicas como, AND (E), OR (OU Inclusivo), XOR (OU Exclusivo) e NOT (Inversor), como também deslocamento a direta ou a esquerda de bits. A tabela abaixo apresenta os operadores bitwise do C++ e suas operações.
Operador | Operação |
---|---|
& | AND (Interseção booleana) |
| | OR (União booleana) |
^ | XOR (Diferença simétrica booleana) |
<< | Deslocamento a esquerda |
>> | Deslocamento a direta |
~ | NOT (Inversor booleano ou complemento) |
As operações na linguagem C++ são escritas de uma forma simples e lógica, os exemplos de código abaixo demonstram alguns trechos de operações bitwise. Tente substituir as variáveis b, c, d com 0 ou 1 para obter resultados diferentes.
// Valores: b = 1, c = 0, d = 0
a = b | c; // a = 1
a = (b & c); // a = 0
a = (b ^ c); // a = 1
a = ~(b & c); // a = 1
a = (b & (d | c)); // a = 0
A Figura 1 mostra a operação d = c & b, sendo que os valores dos 2 conjuntos de 8 bits então passando por uma operação AND ou interseção booleana, formando um novo conjunto de bits ou saída do bitwise.
O conjunto de bits na linguagem é interpretado como um vetor, utilizando a constante bitset<x>, a variável x é o tamanho do conjunto de bits, podendo ser 4 bits, 8 bits, 16 bits ou 32 bits, para utilizar essas funções é necessário incluir a biblioteca bitset. O código em C++ abaixo exemplifica o bitwise da Figura 1.
#include <iostream>
#include <bitset>
using namespace std;
int main(int argc, char** argv)
{
bitset<8> d, c, b;
c = 0b11101111; // 1 conjunto em binário
b = 0b10010001; // 2 conjunto em binário
d = (c & b);
cout << d; // Resultado = 10000001
system("PAUSE");
return 0;
}
As operações de deslocamento de bits são realizadas com a contagem bit a bit da seguinte forma:
- shift-expression << additive-expression
- shift-expression >> additive-expression
O deslocamento (<<) fazem os bits da shift-expression se moverem para a esquerda pelo número de posições do additive-expression. Exemplo: O número 4 em binário é 00000100, aplicando um deslocamento de 2 posições para a esquerda será obtido 00010000 e as posições percorridas serão preenchidas com 0, posteriormente convertendo o resultado para decimal dará 16, ou seja, deslocamentos para a esquerda são equivalentes a uma multiplicação, conhecido como the power of two.
#include <iostream>
#include <bitset>
using namespace std;
int main(int argc, char** argv)
{
bitset<8> bits;
bits = 0b00000100; // 4
bits = (bits << 2);
cout << bits << endl; // Resultado = 00010000 ou 16
system("PAUSE");
return 0;
}
Outra forma de deslocamento para a esquerda é setar o valor 1 no shift-expression ou adicionar o operador OR, sendo assim as posições percorridas não serão mais substituídas por 0, permitindo guardar as posições originais do conjunto modificado.
#include <iostream>
#include <bitset>
using namespace std;
int main(int argc, char** argv)
{
bitset<8> bits, bits2;
bits = 0b01100110; // 1 conjunto em binário
bits2 = 0b01100110; // 2 conjunto em binário
bits = (1 << 4)|(1 << 3);
bits2 |= (1 << 4)|(1 << 3);
cout << bits << endl; // Resultado = 00011000
cout << bits2 << endl; // Resultado = 01111110
system("PAUSE");
return 0;
}
O deslocamento (>>) fazem os bits da shift-expression se moverem para a direita pelo número de posições do additive-expression. Exemplo: O número 22 em binário é 00010110, aplicando um deslocamento de 1 posição para a direita será obtido 00001011 e convertendo o resultado para decimal dará 11. Exemplo 2: O número 5 em binário é 00000101, aplicando um deslocamento de 1 posição para a direita será obtido 00000010 e convertendo o resultado dará 2, ou seja, deslocamentos para a direita são equivalentes a uma divisão por inteiro.
#include <iostream>
#include <bitset>
using namespace std;
int main(int argc, char** argv)
{
bitset<8> bits1, bits2;
bits1 = 0b00010110; // 22
bits2 = 0b00000101; // 5
bits1 = (bits1 >> 1);
bits2 = (bits2 >> 1);
cout << bits1 << endl; // Resultado = 00001011 ou 11
cout << bits2 << endl; // Resultado = 00000010 ou 2
system("PAUSE");
return 0;
}
Entre outras aplicações, conforme o livro de Stroustrup a técnica bitwise é profundamente utilizada nas funções internas da linguagem, como na implementação da biblioteca do C++ ostream, como mostra o exemplo abaixo:
enum ios_base::iostate
{
goodbit=0, eofbit=1, failbit=2, badbit=4
};
// Setando e testando o estado do ostream
state = goodbit;
// ...
if (state&(badbit|failbit)) // Será 0
// Uma função que atinge o end-of-input pode ser representado assim:
// Sendo que todos os bits serão setados como 1.
state |= eofbit;
Referências
- RUSSELL, D. Introduction to embedded systems: using ANSI C and the Arduino development environment. 1 ed. Morgan & Claypool, 2010.
- STROUSTRUP, B. The C++ programming language. 4 ed. Addison-Wesley, 2013.