Representação de Dados Representação binária Exemplo: 1521310 = 111011011011012 Vantagens: • Implementação eletrônica – Possibilidade de armazenar elementos com dois estados – Transmissão eletrônica confiável (robustez a ruídos) – Implementação efienciente de operações artitméticas 0 3.3V 2.8V 0.5V 0.0V 1 0 Byte = 8 bits Faixa de valores em diferentes representações: – Binário: 000000002 111111112 – Decimal: 010 25510 – Hexadecimal 0016 FF16 • Representação na base 16 • Dígitos são ‘0’ - ‘9’ e ‘A’ - ‘F’ • Escreva FA1D37B16 em C como 0xFA1D37B ou 0xfa1d37b 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 Conversão entre bases • De base b para base 10 • De base 10 para base b – Divisões sucessivas por b; a cada iteração i o resto contém o numeral ai void num2string(int num, int base, char *b, int size) { int div, resto; int i = size - 2; div = num; while (div && i >= 0) { resto = div % base; if (resto < 10) b[i] = resto + '0'; else b[i] = (resto -10) + 'A'; div /= base; i--; } } ... char buffer[11]; buffer[10]=0; num2string(num, base, buffer,11); A palavra (word) Cada máquina tem seu tamanho de palavra: =número de bits ocupados por um valor inteiro =número de bits de endereços • A maioria das atuais máquinas tem palavra de 32 bits (4 bytes) • Diferentes tipos de dados (p.ex. char, double) podem ocupar uma fração ou múltiplo do tamanho da palavra, mas sempre um número inteiro de bytes; Tamanhos de Tipos em C …em bytes – Tipo C • • • • • • • • Alpha(64 bit) int long int char short float double long double ponteiro (tipo *) 4 8 1 2 4 8 8 8 32-bit 4 4 1 2 4 8 8 4 Obs: sizeof(T) retona o número de bytes usado por tipo T IA32 4 4 1 2 4 8 10/12 4 Memória orientada a palavras • Memória é um vetor de bytes (cada um com um endereço), mas acesso ocorre palavra a palavra • Endereço corresponde ao primeiro byte da palavra • Endereços de palavras subsequentes diferem de 4 em máquina de 32 bits 32-bit 64-bit Words Words Addr = 0000 ?? Addr = 0000 ?? Addr = 0004 ?? Addr = 0008 ?? Addr = 0012 ?? Addr = 0008 ?? Bytes Ender. 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 0010 0011 0012 0013 0014 0015 Ordenação de bytes Como organizar os bytes de uma palavra (4 bytes)? • Big Endian (computadores Sun, Mac) – byte menos significativo com maior endereço • Little Endian (computadores Alpha, PCs) – Byte menos significativo no menor endereço • Exemplo – variável y tem tem valor 0x01234567 (hexa) – Endereço &y é 0x100 Big Endian 0x100 0x101 0x102 0x103 01 Little Endian 23 45 67 0x100 0x101 0x102 0x103 67 45 23 01 Ordenação de Bytes • Transparente para o programador C • Relevante quando analisamos o código de máquina • Exemplo (litte endian): 01 05 64 94 04 08 add $Ox8049464 %eax • Importante também para a transmissão de dados entre máquinas big e little endian Verificar se a máquina é big ou little endian #include <stdio.h> typedef unsigned char *byte_ptr; void mostra (byte_ptr inicio, int tam) { int i; for (i=0;i<tam;i++) printf("%.2x", inicio[i]); printf("\n"); } void mostra_int (int num) { mostra((byte_ptr) &num, sizeof(int)); } Caracteres • Usa-se uma tabela para mapear inteiros (não negativos) em símbolos • Tabelas mais comuns: – ASCII – codificação em 8 bits ‘a’ ‘z’ 97 122 0x61 0x7A ‘A’ 65 0x41 – UNICODE – codificação em 16 bits • Em C, tipo char define apenas 1 byte • Pode-se manipular um char como um int – Exemplo: if (c > ‘0’) val = c – ‘0’; Operadores bit a bit em C • Podem ser aplicados a qualquer tipo C • Baseados na algebra booleana (1 = true, 0 = false) • And • Or – A & B = 1 quando A=1 e B=1 • Not & 0 1 0 0 0 1 0 1 – ~A = 1 quando A=0 ~ 0 1 1 0 – A | B = 1 quando A=1 ou B=1 | 0 1 0 0 1 1 1 1 • Or exclusivo (Xor) – A ^ B = 1 quando A=1 ou B=1, mas não ambos ^ 0 1 0 0 1 1 1 0 Exemplos int a, b, c; a = 0xFF00; b = 0x00FF; c c c c = = = = a | b; a & b; ~a; a ^b; /* a = 1111 1111 0000 0000 */ /* b = 0000 0000 1111 1111 */ /* c = 0xFFFF */ /* c = 0x0000 */ /* c = 0x00FF */ • Swap sem terceira variável: void troca (int *x, int *y) { *x = *x ^ *y; *y = *x ^ *y; *x = *x ^ *y; } Deslocamento de bits Deslocamento de bits (bit shifting): • x << n: shift p/ esquerda ( equivale a * 2n) • x >> n: shift p/ direita ( equivale a / 2n) – Shift lógico: bits mais significativos preenchidos com bit 0 – Shift aritmético: bits mais significativos preenchidos com o bit mais significativo original. • Exemplo: short int a, b, c; a = 0xF; b = 0x3C; c = a << 4; c = b >> 2; /* /* /* /* a b c c = = = = 0000 0011 0xF0 0000 1111 */ 1100 */ */ 1111 = 0xF */ Representação de tipos estruturados: arrays • Alocação contígua na memória • Primeiro elemento (a[0]) corresponde ao menor endereço de memória • Tipo a[tam] ocupa sizeof(Tipo)*tam bytes • O nome do array equivale a um ponteiro (cujo valor não pode ser alterado) • Exemplo: int a[tam], a é ponteiro constante tipo int *, e seu valor é &a[0] • Nomes de arrays passados como parâmetros são endereços (do array) Aritmética básica com ponteiros Ponteiro é um endereço para um tipo específico de dado. P.ex. int *, char *, etc. Seja a declaração T *pa • Expressão *pa significa objeto T apontado por pa. E pa significa (endereço de objeto). • Expressão (pa +i) = pa + sizeof(T)*i • Exemplo: se int *pi, então (pi +3) é um deslocamento de 12 bytes do endereço pi. • O que significam? pa++, ++pa, (*p)++ • Comparação entre ponteiros (p == q, p >q ) só se são para o mesmo tipo • Subtração entre dois ponteiros, (p1 – p2), indica o número de elementos entre p1 e p2 • Existe equivalência entre arrays e ponteiros Alocação para Arrays • Seja T A[L]; – Array de tipo T e comprimento L – Alocação contígua de L * sizeof(T) bytes • Exemplos: char string[12]; x x + 12 int val[5]; x x+4 x+8 x + 12 x + 16 x + 20 double a[4]; x x+8 x + 16 char *p[3]; x x+4 x+8 x + 24 x + 32 Arrays Aninhados #define PCOUNT 4 typedef int vetor[5]; vetor a[PCOUNT] = {{1, 5, 2, 0, 6}, {1, 5, 2, 1, 3 }, {1, 5, 2, 1, 7 }, {1, 5, 2, 2, 1 }}; vetor a[4]; 1 5 2 0 6 1 5 2 1 3 1 5 2 1 7 1 5 2 2 1 76 96 116 136 156 – Declaração “vetor a[4]” equivalente a “int a[4][5]” • Variável a denota array de 4 elementos – Alocados continuamente • Cada elemento é um vetor de 5 int’s – Alocados continuamente – Ordenação é por linha da matriz (elementos do vetor mais externo) Alocação de Arrays Aninhados • Declaração T A[R][C]; – Tipo T – R linhas, C colunas – Elemento do tipo T ocupa K bytes • Tamanho do Array – R * C * K bytes • Ordenação principal por linha A[0][0] • • • • • • A[0][C-1] • • • A[R-1][0] • • • A[R-1][C-1] int A[R][C]; A A A A [0] • • • [0] [1] • • • [1] [0] [C-1] [0] [C-1] 4*R*C Bytes • • • A A [R-1] • • • [R-1] [0] [C-1] Estruturas Heterogêneas (structs) O alinhamento de structs na memória segue as regras: 1. Os campos são alocados na ordem em que aparecem na declaração; 2. Cada campo do struct que corresponde a uma palavra (ou múltiplas palavras) deve ser alocado em um endereço múltiplo de 4; shorts em múltiplos de 2 3. O início de cada estrutura tem que estar sempre alinhado em endereços múltiplos de 4 Exemplo: struct s{ sizeof(s1) = 8; char c[2]; int i; } Ocupação dos bytes: c[0] x c[1] struct s s1; i Padding = X+4 bytes não usados i i i Padding no final • Para garantir o alinhamento do struct em um endereço múltiplo de 4, pode ocorrer um padding no final. • Exemplo: struct s{ int i; char c[2]; } sizeof(h) = 16; struct s h[2]; h[0] i i i i x h[1] c[1] X+4 i x+8 c[0] i i i c[0] X+12 c[1] Exemplo de Alinhamento de structs struct s{ char c,d; int a; char e } struct s s1; c d x a a a struct s{ short c,d; char e; short a } struct s s2; a e X+4 sizeof(s1) = 12 X+8 c c d d e x X+4 sizeof(s2) = 8 a a X+8