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 1 0 3.3V 2.8V 0.5V 0.0V 1 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 al im ary c x n He De Bi 0 0 0000 1 1 0001 2 2 0010 3 3 0011 4 4 0100 5 5 0101 6 6 0110 7 7 0111 8 8 1000 9 9 1001 A 10 1010 B 11 1011 C 12 1100 D 13 1101 E 14 1110 F 15 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); 2 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 • • • • • • • • • int long int char short float double long double tipo * (*) 64 bits Alpha(*) 32-bit IA32 4 8 1 2 4 8 8 8 4 4 1 2 4 8 8 4 4 4 1 2 4 8 10/12 4 Obs: sizeof(T) retona o número de bytes usado por tipo T 3 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 ?? Bytes Ender. Addr = 0000 ?? Addr = 0004 ?? Addr = 0008 ?? Addr = 0008 ?? Addr = 0012 ?? 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 x tem tem valor 0x01234567 (hexa) – Endereço &x é 0x100 Big Endian 0x100 0x101 0x102 0x103 01 Little Endian 23 45 67 0x100 0x101 0x102 0x103 67 45 23 01 4 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 • 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)); } 5 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’ ‘A’ 97 122 65 0x61 0x7A 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) • Or • And – 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 6 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 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 0s – Shift aritmético: bits mais significativos preenchidos com 1s • 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 */ 7 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 8 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+8 x+4 x + 12 x + 16 x + 20 double a[4]; x x+8 x + 16 x + 24 x + 32 char *p[3]; x x+4 x+8 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) 9 Alocação de Arrays Aninhados • Declaração T A[R][C]; A[0][0] – 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][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] • • • A A [R-1] • • • [R-1] [0] [C-1] 4*R*C Bytes 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 i i i Padding = X+4 bytes não usados 10 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]; i h[0] i i i x c[0] c[1] X+4 h[1] i i i x+8 i c[0] c[1] X+12 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 a a X+8 sizeof(s2) = 8 11