Baixe o app para aproveitar ainda mais
Prévia do material em texto
Tipos de Dados Prof. Saulo C. Campos Valores e Tipos de dados • As LP de programação usam o conceito de tipos de dados para definir como os valores serão representados. Valor é sinônimo de dado. • Cada LP tem um conjunto de tipos de dados definidos. • Um tipo de dado faz referencia um conjunto de valores que tem um comportamento uniforme nas operações associadas a tal tipo. • Os tipos são divididos em dois grupos: primitivos e compostos. Tipos primitivos • São os tipos cujo os valores não podem ser decompostos em outros valores (de tipos mais simples). • A partir dos tipos primitivos, os demais tipos são formados. • Os tipos primitivos de uma linguagem são definidos durante a fase de projeto e podem indicar o objetivo da linguagem. Ex.: Fortran. • Tipos primitivos: inteiro, caractere, booleano, decimal, ponto flutuante e enumerado. Tipo Inteiro • Corresponde a um faixa de intervalos do conjunto dos números inteiros. • Em uma LP existem vários tipos inteiros, sendo que o mais comum é determinado pelo hardware, como o tipo int da linguagem C. • Por exemplo a linguagem C possui 8 tipos de inteiros decorrentes da combinação dos tipos básicos char e int com os modificadores signed, unsigned, short e long. Tipo Inteiro • Modificadores do tipo inteiro. – signed: conjunto de valores com números negativos. – unsigned: conjunto de valores sem números negativos. – short: inteiros armazenados em 16 bits. – long: inteiros armazenados em 32 (ou 64) bits. • O tipo int (da linguagem C) pode armazenar 16, 32 ou 64 bits, sendo determinado pelo palavra do computador e pelo compilador. Fato privilegia a eficiência e prejudica a portabilidade. • O tipo char armazena 8 bits. Tipo Inteiro • Modificadores do tipo inteiro. – signed: conjunto de valores com números negativos. – unsigned: conjunto de valores sem números negativos. – short: inteiros armazenados em 16 bits. – long: inteiros armazenados em 32 (ou 64) bits. • O tipo int (da linguagem C) pode armazenar 16, 32 ou 64 bits, sendo determinado pelo palavra do computador e pelo compilador. Fato privilegia a eficiência e prejudica a portabilidade. • O tipo char armazena 8 bits. Algumas das combinações de tipo inteiro da Linguagem C Tipo Tamanho (bits) Intervalo char 8 -128 127 unsigned char 8 0 255 signed char 8 -128 127 int 32 -2.147.483.648 2.147.483.647 unsigned int 32 0 4.294.967.295 signed int 32 -2.147.483.648 2.147.483.647 short int 16 -32.768 32.767 long int 32 -2.147.483.648 2.147.483.647 unsigned short int 16 0 65.535 unsigned long int 32 0 4.294.967.295 long long int 64 -9.223.372.036.854.775.807 9.223.372.036.854.775.806 Tipo Inteiro na Java Tipo Tamanho (bits) Intervalo byte 8 -128 127 short 16 -32.768 32.767 int 32 -2.147.483.648 2.147.483.647 Long 32 -9.223.372.036.854.775.807 9.223.372.036.854.775.806 Tipo Inteiro • A representação de inteiros unsigned é a notação binária tradicional, porém a representação de inteiros signed utiliza-se a representação de Complemento a Dois. Representação Notação Complemento a Dois Notação Binária 1111 1111 -1 255 1111 1100 -4 252 0000 0001 1 1 0000 0101 5 5 Tipo Inteiro • Considere que uma célula de memória armazena o valor 1111 1101 correspondente a um tipo inteiro signed. Qual seu valor decimal? Representação na notação de complemento a dois: 1111 1101 Passo 1: Verificar o dígito mais à esquerda: 1 (negativo) Passo2: Fazer inversão binária: 0000 0010 Passo 3: Somar 1 (0000 0001) na representação binária invertida: 0000 0011 Passo 4: Efetuar a transformação do número para base decimal: 3 Passo 5: Considerar o sinal encontrado no passo 1: -3 Resultado final: Tipo Inteiro • A cardinalidade no tipo inteiro é 2n, sendo n a quantidade de bits para representação. Tipo Caractere • Apesar de ser usado para representar símbolos são armazenados como inteiros. • Algumas linguagens como PASCAL e MODULA2, oferecem o tipo primitivo char, que corresponde aos símbolos da tabela de caracteres mais comum: ASCII – American Standard Code for Information Interchange. • A tabela ASCII não inclui caracteres acentuados e possui dígitos de controle. Também existe outras tabelas de caracteres como EBCDIC e UNICODE. Tipo Caractere • Obviamente a tabela ASCII e ASCII Estendida não possui todos os caracteres usados no mundo. Isso acarreta em diversos problemas. • Para resolve-los criou-se a tabela UNICODE, que endereça 16 bits e armazena praticamente todos os caracteres utilizados em linguagens naturais. • Para manter a compatibilidade com outras linguagens a tabela UNICODE armazena a tabela ASCII (ou seja, mantem os mesmos endereços). • Java adota a tabela UNICODE como padrão. • O tipo primitivo char da linguagem C é tratado como inteiro. Sobre ele é realizado as mesmas operações que qualquer inteiro e sua cardinalidade é correspondente a tabela ASCII. Por esse motivo a implementação abaixo é válida: char d; d = 3 + ‘a’; • Isso torna a linguagem mais redigível. Em PASCAL para obter o código ASC de um caractere é necessário chamar uma função, enquanto em C: printf(‘%d’, d); printf(‘%c’, d); Tipo Caractere • Tipo mais simples de uma LP. Cardinalidade = 2. • com exceção da linguagem C, a maioria das LP possuem este tipo, que é armazenado em 1 byte. • A linguagem C trata valores numéricos como condicionais. Neste caso, valores diferentes de zero são considerados true. Enquanto valor igual a zero é false. if (c += 1) x = 10; • A expressão acima pode ser um erro, mas será válida em C. Por isso, Java adotou o tipo boolean. Tipo Booleano • Armazena de forma precisa números não inteiros com um número fixo de casas decimais. • O tipo decimal é importante em aplicações comerciais, justamente por armazenar precisamente as casas decimais (quantidade limitada), o que não é possível com tipos de ponto flutuante. • Seu armazenamento é realizado pela notação BCD (Binary coded decimal), utilizando 1 byte para cada 1 dígito ou 2 dígitos (qualquer dígito é representado por 4 bits). Tipo Decimal • Representação BCD – 8 bits para representação de até 4 casas decimais. – 28 bits para representação da parte inteira. – 4 bits para representação do sinal. – Total de 6 bytes 2.733.056,6213 Tipo Decimal 0000 0010 0111 0011 0011 0000 0101 0110 0110 0010 0001 0011 sinal 4 bytes 7 casas inteiras – 28 bits Sinal – 4 bits 2 bytes 4 casas decimais • A cardinalidade desse tipo é 2 X 10n (Porque?). Onde n é a quantidade de dígitos inteiros e fracionários. • A desvantagem do uso do tipo decimal é justamente sua representação, que trata um intervalo de valores pequeno em relação a quantidade de memória necessária para o armazenamento. Tipo Decimal • Modela números reais em ponto flutuante. Ex.: 1 = 0,01*102 = 100,0*10-2. • Porém o armazenamento da parte decimal é limitado e por isso armazenado de forma aproximada. • A representação para armazenamento combina representações binárias de frações e expoentes. • As operações de ponto flutuante são complexas e oferecidas pelo hardware da máquina em diversos padrões (o que foge do escopo da aula). • O padrão mais comum é o IEEE 754. Tipo Ponto Flutuante • Exemplo simples: – Número decimal: 5,75 – Número convertido para binário: 101,11 – Normalizando para notação cientifica: 0,10111 x 23 – Sinal = 0 (positivo) – Expoente = 011(3) – Fração = 10111 – Representação binária: 0000 1100 0001 0111 • Pelo fato da notação cientifica poder ser feita de várias maneiras diferentes adotou-se a convenção de que o número mais significativo nunca deve ser zero. Tipo Ponto Flutuante • Há representações de precisão simples (32 bits) e dupla (64 bits). Em Java por exemplo, corresponde respectivamente aos tipos float e double. Tipo Ponto Flutuante Precisão simples (32 bits) Precisão dupla (64 bits) 8 bits 23 bits 11 bits 52 bits • Existe representações especiais para zero, números inexistentes e infinito. • A cardinalidade do tipo ponto flutuante é a combinação entre o total de bits utilizado na representação. Por exemplo, para precisão simples é 232. Tipo Ponto Flutuante • Em Linguagens como PASCAL, ADA, C e C++ é permitido criar um novo tipo primitivo a partir da enumeração de identificadores que irão denotar os valores do novo tipo. Enum diaUtil{seg, ter, qua, qui, sex}; • Os tipos enumerados possuem correspondia direta com intervalos de tipos inteiros, sendo assim podem ser usados como índices de vetores ou em testes condicionais. • Os tipos enumerados aumentam a legibilidade e a confiabilidade. Tipo Enumerado • Em C e C++ os valores enumerados são convertidos implicitamente para números inteiros, sendo aplicados sobre eles as mesmas operações. • A cardinalidade do tipo enumerado corresponde a quantidade de elementos na sequencia. Tipo Enumerado • São tipos formados a partir da combinação de tipos primitivos. • Tipos compostos são definidos pelo programador. • Estão divididos em: – Produto Cartesiano – Uniões – Mapeamentos – Conjuntos potência – Tipos recursivos Tipos Compostos • Combinação de valores de tipos diferentes em tuplas. Ex.: struct (C e C++) e record (Pascal e ADA). struct aluno { int matricula; char nome[30]; }; Struct turma { int ano; struct aluno listAlunos[50]; } Periodo3; Produto Cartesiano • Acesso a elemento geralmente é feito de maneira completa e separada por ponto. Periodo3.aluno[0].matricula = 123; • Há linguagens que possuem métodos para simplificar o acesso, como PASCAL with Periodo3.aluno[0] do begin matricula := 123; nome := ‘Saulo’; end; Produto Cartesiano • Cardinalidade deste tipo é dada pelo produto cartesiano da quantidade de seus componentes. #S x #T • No caso em que o tipo é homogêneo (mesmo tipo de componentes) a cardinalidade é n (quantidade de componentes) • Em linguagens orientadas a objetos o produto cartesiano existe somente por questões de compatibilidade, pois este conceito é substituído pelo conceito de classes. Produto Cartesiano • Consiste na união de valores distintos para formar um novo tipo de dados, porém nas uniões livres pode haver interseção entre o conjunto de valores. • Isso quer dizer que em uma união livre todos os elementos são armazenados no mesmo endereço de memória. • O tamanho do espaço de memória é igual ao tamanho do tipo de maior tamanho. • A vantagem é poder acessar o mesmo dado de formas diferentes. Uniões Livres union Identificacao { int Abreviada; char Completa[10]; }; void main(){ union Identificacao Mat; Mat.Abreviada = 122; printf("Matricula Abreviada: %d\n",Mat.Abreviada); printf("Matricula Abreviada: %s\n",Mat.Completa); strcpy(Mat.Completa, "ALN-122-3"); printf("Matricula Abreviada: %d\n“,Mat.Abreviada); printf("Matricula Abreviada: %s\n",Mat.Completa); } Uniões Livres • Nas uniões disjuntas não há interseção entre os valores dos tipos que formam a união. • É utilizado uma tag (identificador) para diferenciar qual o tipo originário do valor da união. • São exemplos de linguagens que suportam esse tipo composto: PASCAL, MODULA2 e ADA. Uniões Disjuntas type Precis=(exato,aprox); Numero = record case P: Precis of exato: (VlrInt:integer); aprox: (VlrRl :real); end; var X: Numero; begin X.P := exato; X.VlrInt := 3; end;. Uniões Disjuntas • Tipos de dados cujo conjunto de valores corresponde a todos os possíveis mapeamentos de um tipo de dados S em outro T (o qual pode ser o próprio S). • A notação S -> T simboliza o conjunto de todos os possíveis mapeamentos distintos de S para T. Mapeamentos • São mapeamentos onde o conjunto domínio é finito. • Arrays são mapeamentos finitos de um conjunto de índices S para um conjunto de componentes T. Mapeamentos Finitos • Os arrays podem ser classificados quanto o seu tamanho e seu tempo de definição do vetor e o local onde fica armazenado: – Array Estático – Array semi-estático - pilha – Array semi-dinâmico - pilha – Array dinâmico – Heap • Há também vetores multidimensionais conhecidos como matrizes, que é acessado por um conjunto de n índices. Mapeamentos Finitos • Mapeamentos por funções definem uma relação S -> T definida por uma função (subprograma). • Algumas LP suportam tipos definidos por funções, sendo eles valores de primeira e segunda classe: – Primeira classe: quando se pode executar as mesmas operações de qualquer tipo de dado. Ex.: criar variáveis, criar estrutura de dados, passar valores, parâmetros... – Segunda classe: Quando existem restrições sobre a utilização de algum desses recursos. Ex.: PASCAL permite a passagem de funções por parâmetro mas não a criação de variáveis. Mapeamentos por funções • A LP C consegue tratar os mapeamentos por funções como valores de primeira classe através do uso de ponteiros. Já o Java não trata este tipo. int impar (int n) {return n%2;} int negativo (int n) {return n < 0;} int conta ( int x[], int n, int(*p) (int) ) { int j = rand%n-1; return ( (*p) (x[j]) ); } void main( ) { int vet[5] = {1, 2, 3, 4, 5}; printf(“%d\n”, conta( vet, 5, impar)); printf(“%d\n”, conta( vet, 5, negativo)); } Mapeamentos por funções • São tipos onde seus valores são definidos por todos os subconjuntos de um conjunto S. • LP que suportam esse tipo: PASCAL e MODULA2. • Algumas LP orientadas a objetos implementam este tipo através de classes, como Java e Smaltalk. Conjunto potência • A vantagem deste tipo é poder usar as operações de conjunto: Diferença, Contém, simetria, Está contido, Interseção, União e outras. type TCorBasica=(Vermelho, Amarelo, Azul); TCorPrimaria = set of CorBasica; TDigitos = set of 0..9; var Cor: TCorBasica; begin Cor = Vermelho; if (Cor in TCorPrimaria) writeln(‘É uma cor primária’); end; Conjunto potência • As operações são baseadas em representação binária que indica quais valores estão presentes no conjunto. var S: set of [‘a’..‘f’]; begin S := [‘a’,’b’] + [‘d’]; end; Conjunto potência S a b c d e F 1 1 0 1 0 0 • São tipos de dados cujos valores são compostos por valores do mesmo tipo. • Em C, C++, PASCAL e ADA este efeito é realizado através de ponteiros. Já em Linguagens OO são realizados diretamente pela declaração de classes. Tipos Recursivos struct no { int elem; struct no* prox; }; class no { int elem; no prox; } • Strings correspondem a uma sequencia de caracteres. • Algumas LP como PERL, SNOBOL e ML as tratam como tipos primitivos. • C e PASCAL tratam como mapeamentos finitos. E geralmente as funções sobrestring são as mesmas para vetores de inteiros. • Java considera um novo tipo, porém é definida por uma classe da biblioteca padrão. • PROLOG e LISP tratam com uma lista recursiva. Strings • Dependendo do tratamento dado ao tipo string de acordo com a implementação da LP, elas podem ser: – Estática: Tamanho pré-estabelecido e definido em compilação. Não é modificado durante a execução. – Dinâmica limitada: Tamanho máximo é predefinido, mas pode variar durante a execução do programa até o tamanho máximo. A linguagem C usa ‘\0’ para marcar o final da string e PASCAL usa o índice zero para informar o tamanho. – Dinâmica: Tamanho pode variar livremente ao longo da execução do programa. Ex.: LISP, PERL e DELPHI. Strings
Compartilhar