[CAPÍTULO 1] - [CAPÍTULO 2] - [CAPÍTULO 3]
[PARTE 1] - [PARTE 2] - [PARTE 3] - [PARTE 4] - [PARTE 5] - [PARTE 6] - [PARTE 7] - [PARTE 8]
[PARTE 1] - [PARTE 2] - [PARTE 3] - [PARTE 4] - [PARTE 5] - [PARTE 6] - [PARTE 7] - [PARTE 8]
Operadores e Expressões: bit-a-bit e especiais
Objetivos:
Em C, OPERADORES e EXPRESSÕES podem ser classificados em cinco grande categorias:- Atribuições
- Aritméticos
- Lógicos e Relacionais
- Bit-a-Bit
- Especiais
- Bit-a-Bit
- Especiais
Manipulação de Bits:
OPERADORES e EXPRESSÕES de manipulação de bits ( bit-a-bit ) referem-se a testar, atribuir ou deslocar os bits efetivos em um byte ou palavra que correspondem aos tipos básicoschar
, int
e variantes.OBS.:
- Operações bit-a-bit não podem ser feitas sobre
float
,double
,long double
,void
ou outros tipos mais complexos.
Significados:
- Os operadores
&
,|
e~
têm a mesma tabela verdade que&&
,||
e!
(respectivamente), com a diferença que operam sobre os bits individuais da representação de dois números (ou apenas um, no caso de~
) e não sobre expressões numéricas. - o operador
^
tem a mesma tabela verdade que a funçãoxor()
discutida na aula passada (novamente operando sobre bits individuais e não sobre expressões numéricas). - o operador
>>
desloca os bits de um número k casas para à esquerda fazendo com que os k bits mais à esquerda se percam e os k bits mais à direita sejam '0' - o operador
<<
desloca os bits de um número k casas para à direita fazendo com que os k bits mais à direita se percam e os k bits mais à esquerda sejam '0'
<<
e >>
):Considere:
unsigned int x = 7;Se pudéssemos olhar a representação do valor
7
(decimal) 'dentro' da variável x
, veríamos os seguinte padrão de bits: x = 0...00000111
( a quantidade total de bits depende de quantos bytes são utilizados para armazenar um unsigned int
, quantidade que depende da máquina alvo e compilador ).A EXPRESSÃO abaixo (deslocamento à esquerda):
x = x << 2;faz com que o padrão de bits representando o valor inicial
7
se transforme em x = 0...00011100
. Quer dizer, os bits, todos foram deslocados para a esquerda dois bits! Assim, o valor agora reresentado na variável x
é 28 !De maneira geral, se uma variável tem o seguinte padrão de bits:
- x = | bn-1 | bn-2 | ... | b2 | b1 | b0 |
- onde:
- a variável é representada por n bits
- cada um dos bi (para 0 ≤ i ≤ n-1 ) denota um bit com valor '0' ou '1'
-
x << k
, resulta em:- x = | bn-k-1 | bn-k-2 | ... | b2 | b1 | b0 | 0 | 0 | .. | 0 |0 | 0 |
- onde a seq. final de '0's contém k '0's
-
x >> k
, resulta em:- x = | 0 | 0 | ... | 0 | 0 | 0 | bn-1 | bn-2 | ... | bk-1 | bk |
- onde a seq. inicial de '0's contém k '0's
Exemplo 2 (
~
):Continuando o exemplo anterior, tem-se
x = 0...00011100
. A EXPRESSÃO abaixo ( complemento de 1 ):
x = ~ x;tem o seguinte efeito na representação de x em binário:
x = 1...11100011
. Ou seja, os bits que são '0' passam a ser '1', os que são '1' tornam-se '0'. Exemplo 3 (
|
):Continuando o exemplo anterior, tem-se
x = 1...11100011
.A EXPRESSÃO abaixo ( OR ):
x = x | 8 ;transforma x em :
x = 1...11101011
. Quer dizer, muda o quarto bit (da direita para a esquerda) de '0' para '1'. Isto acontece por que:x = 1...11100011 4 = 0...00001000 -------------- x | 4 = 1...11101011 /* OU bit a bit */
-
|
é frequentemente utilizado para LIGAR bits particulares
Exemplo 4 (
&
):Continuando o exemplo anterior, tem-se
x = 1...11101011
.A EXPRESSÃO abaixo ( AND ):
x = x & 0xf...f7 ;transforma x em :
x = 1...11100011
. Quer dizer, muda o quarto bit (da direita para a esquerda) de '1' para '0'. Isto acontece por que:x = 1...11101011 0xf...f7 = 1...11110111 -------------- x | 0xf...f7 = 1...11100011 /* AND bit a bit */
-
&
é frequentemente utilizado para DESLIGAR bits particulares
Aplicações Típicas:
- rotinas de SO, drivers de dispositivos
- criptografia
- compactação de dados
Atividade 1:
Tendo em vista os significados de|
e &
que são, respectivamente, similares aos de ||
e &&
, descreva - através de um exemplo - o significado de ^
que é similar à função xor()
descrita na última aula.Atividade 2:
Uma tabela binária é uma tabela (matriz linhas x colunas ) onde cada célula armazena um dentre dois valores possíveis. Por exemplo, a Freqüência deste curso é em essência uma tabela binária uma vez que cada célula só pode conter ou '0' (presente) ou '2' (ausente).Uma maneira simples de representar uma tabela binária é através da seguinte estrutura:
char tabela1[40][10];Nesta representação são gastos : 40*10 = 400 bytes (
sizeof char = 1 byte
).Uma maneira mais eficiente (do ponto de vista da quantidade de memória utilizada) seria utilizar um mapa de bits para armazenar a tabela binária. Tal mapa de bits é uma sequencia de bits mantidas pelos operadores bit a bit.
Para a tabela acima de 40 linhas por 10 colunas poderíamos ter a seguinte declaração:
char tabela2[ (40*10) / 8 ];Nesta representação são gastos : (40*10)/8 = 50 bytes. A idéia aqui é, ao invés de usar um
char
(1 byte) para armazenar 0 ou 1, usar um bit individual dentro de um char
. Vejamos como isto pode ser feito.Atribuindo valor a uma determinada célula (linha,coluna)
Na primeira representação acima, uma dada célula pode ter seu valor modificado da seguinte maneira:
tabela1 [3][5] = 0; /* aluno 3 esteve presente */ tabela1 [7][5] = 2; /* aluno 7 faltou */
Na segunda representação acima, o mesmo processo tem de ser simulado através de uma função:
void set(unsigned x, unsigned y, int val) { unsigned pos = ( x* 10 + y ) / 8; unsigned des = ( x* 10 + y ) % 8; if (val) tabela2[pos] |= ( 1 << des ); else tabela2[pos] &= ~( 1 << des ); }
Recuperado o valor em uma determinada célula (linha,coluna)
A consultar do valor em uma dada célula é feita de maneira direta para a primeira representação (utilizado índices linha e coluna). Já para a segunda representação devemos fazer uma função de consulta.
/* retorna 0 se na pos x,y está armazenado 0 retona 1 se na pos x,y está armazenado 1 **/ int get(unsigned x, unsigned y) { ... }Como exercício, escreva a função de consulta
get()
.Operadores Especiais:
- Operador Condicional
? :
- Operador
sizeof
- Operadores de Conversão de Tipo ou Casting
(tipo)
- Operador Vírgula
,
- Operadores
()
e[]
Serão vistos em mais detalhes em aulas futuras:
- Operadores de ponteiros
*
e&
- Operadores de estruturas
.
e->
Operador Condicional ? :
UsoEXP1 ? EXP2 : EXP3;Significado
EXP1 é avaliada ... Se ela for verdadeira, então EXP2 é avaliada e se torna o valor da expressão como um todo. ... Se EXP1 for falsa, então EXP3 é avaliada e se torna o valor da expressão como um todo.
Por exemplo, o comando condicional da função
set()
da atividade 2 poderia ser reescrito assim:tabela2[ pos ] = val ? tabela2[pos] | (1 << des) : tabela2[pos] & ~ (1 << des) ;
b) Operador sizeof
Usosizeof ID-VAR; sizeof (ESP-TIPO);Significado
sizeof
é um operador unário em tempo de compilação que retorna o tamanho, em BYTES, de uma variável ou de um especificador de tipo de dados, este último escrito entre parênteses.Por exemplo, para tornar o código da função
set()
da atividade 2 mais PORTÁVEL, podemos fazer:char tabela2 [ (40*10) / ((sizeof char)*8) + 1 ]; void set(unsigned x, unsigned y, int val) { unsigned pos = ( x* 10 + y ) / ((sizeof char)*8); unsigned des = ( x* 10 + y ) % ((sizeof char)*8); ... }
c) Conversão Explícita de tipos (casting)
Uso(ESPECIFICADOR-DE-TIPO) EXPRESSÃO;Significado
Força - explicitamente - uma EXPRESSÃO a ser de um determinado tipo dado por
ESPECIFICADOR-DE-TIPO
.Por exemplo, para deixar claro que em:
tabela2 |= ( 1 << des);o valor
1
se trata de um char
( 1 byte ) podemos fazer de forma explícita:tabela2 |= ( ((char) 1) << des );
Além de conversões explícitas, o compilador realiza algumas conversões implícitas ...
Conversões Automáticas
Exemplo:
d) Operador vígula ,
UsoEXP1, EXP2;Significado
O operador vígula é usado para encadear diversas expressões. O lado esquerdo (EXP1) é sempre avaliado como
void
. Assim, a expressão do lado direito (EXP2) torna-se o valor de toda a expressão separada por vírgula.Por exemplo:
int x,y; x = ( y = 3, y+1 );primeiro o valor 3 é atribuído a
y
e, em seguida, o valor 4 é atribuído a x
. Os parênteses são necessários porque o operador vírgula tem a menor precedência de todos os operadores de C. Um outro exemplo é:
int i,x; for ( i = 0 , x = 1; i < 10; i++ , x+=2) { ... }
e) Operadores () e []
- Parênteses
()
são operadores que aumentam a precedência das operações dentro deles. - Colchetes
[]
realizam indexação de vetores e matrizes.
Quadro Geral de Operadores em C:
A tabela 2.8 lista a precedência de todos os operadores de C. Note que todos os operadores, exceto os operadores unários e o?
, associam da esquerda para a direita. Os operadores unários ( *
, !
e ~
) e o ternário ?
associam da direita para a esquerda.Ordem de Avaliação:
O padrão C ANSI (C89) não estipula que as subexpressões de uma expressão devam ser avaliadas e uma ordem específica. Assim, seu código nunca deve contar com a ordem em que as subexpressões são avaliadas. Por exemplo, a expressão:x = f() + g();não garante que
f()
será chamada antes de g()
!Leituras Recomendadas:
- CCT capítulo 2
- K&R capítulos 2
- http://pt.wikipedia.org/wiki/Operadores_em_C_e_C%2B%2B
- http://www.mspc.eng.br/info/cpp_oper_10.shtml
Exercícios
- Realize por completo a atividade 1
- Realize por completo a atividade 2, escrevendo a função
get()
e testando as funçõesset()
eget()
em um programa que lê do usário posições x e y, liga os bits nestas posições e ao final imprime a tabela completa. - Observe o padrão abaixo:
- ( (0000 0001)2
<<
1 ) → (0000 0010)2 = 2 - ( (0000 0010)2
<<
1 ) → (0000 0100)2 = 4 - ( (0000 0100)2
<<
1 ) → (0000 1000)2 = 8 - ( (0000 1000)2
<<
1 ) → (0001 0000)2 = 16 - ( (0001 0000)2
<<
1 ) → (0010 0000)2 = 32 - ...
- desse padrão, o que se pode concluir com relação à operação de deslocamento à esquerda?
- e com relação à operação de deslocamento à direita?
- em termos numéricos o que significa x
<<
k ? Quer dizer, se x vale 5 quanto irá valer x<<
k ? - em termos numéricos o que significa x
>>
k ? Quer dizer, se x vale 35 quanto irá valer x>>
k ?
- ( (0000 0001)2
- Para o programa abaixo, descreva como ele funciona e qual a saída gerada após a sua execução.
#include <stdio.h> int g(float a, float b) { static float f; return f += a > b ? a / b : b / a , (int) f; } int main() { printf("%i\n", g(7,2)); printf("%i\n", g(2,3)); }Bibliografia e fonte:
- [CCT] Schildt, H. (1996) C, completo e total: 3a Ed.. São Paulo, Makron.
- LP, UFMA; Coutinho, Lucian. Linguagem de programação para ciencia da computação da ufma.http://www.deinf.ufma.br/~lrc/2009.1/LP/
- [K&R] KERNIGHAN, B. e RITCHIE, D. (1990) C, a linguagem de programação: padrão ANSI. Rio de Janeiro: Campus.
- DEITEL, H. M. (1999) Como programar em C. Rio de Janeiro: LTC.
- Módulo Consultoria e Informática (1989) Linguagem C: programação e aplicações. Rio de Janeiro: LTC.
Coloque aqui o seu email