Baixe o app para aproveitar ainda mais
Prévia do material em texto
GCC 105 – LINGUAGENS DE PROGRAMAÇÃO I AULA 4 – Tipos de Dados II 1º Semestre de 2015 Prof. Janderson Rodrigo de Oliveira Universidade Federal de Lavras Departamento de Ciência da Computação Uniões • Uma união é um tipo cujas variáveis podem armazenar diferentes valores de tipos em vários momentos durante a execução de um programa. • Em outras palavras, com uma união é possível suportar dados diferentes, alocados no mesmo espaço de memória, em momentos diferentes. • Fortran, C e C++ fornecem construções para representar uniões nas quais não existe um suporte da linguagem para verificação de tipos. As uniões nessas linguagens são chamadas uniões livres. Uniões • Exemplo de união em C: union tipo{ int inteiro; float real; }; union tipo elemento; ... elemento.real = 1.35; elemento.inteiro = 27; Uniões • Exemplo de união em C sem a verificação de tipo: union tipo{ int inteiro; float real; }; union tipo elemento; float x; ... elemento.inteiro = 27; x = elemento.real; Uniões • A última atribuição não é verificada em relação ao seu tipo, porque o sistema não pode determinar o tipo atual do valor de elemento, então ele atribui o valor 27 para a variável float x, o que obviamente não faz sentido. • A verificação de tipos união requer que cada construção de união inclua um indicador de tipo. Tal indicador é chamado de etiqueta ou discriminante, e uma união com um discriminante é chamada de união discriminada. – Primeira linguagem a oferecer união discriminada: ALGOL 68. Uniões • Uniões podem ser potencialmente inseguras em algumas linguagens, por não permitirem que as referências para suas uniões sejam verificadas em relação aos seus tipos (Fortran, C e C++). • Exemplo de linguagem que utiliza uniões de forma segura: Ada. • Java e C# não incluem uniões. Uniões – Exemplo em C #include <stdio.h> union Tipo{ int valorInteiro; float valorReal; }; intmain(){ //Parte 1 union Tipo exemplo; exemplo.valorInteiro = 10; printf("Mensagem 1: %d\n", exemplo.valorInteiro); exemplo.valorReal = 3.5; printf("Mensagem 2: %.2f\n", exemplo.valorReal); printf("Mensagem 3: %d\n", exemplo.valorInteiro); return 0; } Uniões – Exemplo em C • Resultado Uniões – Exemplo em C #include <stdio.h> union Tipo{ int valorInteiro; float valorReal; }; intmain(){ //Parte 1: ... //Parte 2 printf("Endereco 1: %d\n", &exemplo.valorInteiro); printf("Endereco 2: %d\n", &exemplo.valorReal); return 0; } Uniões – Exemplo em C • Resultado Uniões – Exemplo em C #include <stdio.h> union Tipo{ int valorInteiro; float valorReal; }; intmain(){ //Parte 1: ... //Parte 2: ... //Parte 3 exemplo.valorInteiro = 1.0; printf("Mensagem 4: %d\n", exemplo.valorInteiro); printf("Mensagem 5: %.2f\n", exemplo.valorReal); return 0; } Uniões – Exemplo em C • Resultado Ponteiros e Referências • Um tipo ponteiro é um no qual as variáveis têm uma faixa de valores que consistem em endereços de memória e um valor especial, nil. • O valor nil não é um endereço válido e é usado para indicar que um ponteiro não pode ser usado atualmente para referenciar uma célula de memória. • Ponteiros são projetados para dois tipos de uso. Ponteiros e Referências • Ponteiros são projetados para dois tipos de uso: 1. Endereçamento indireto, frequentemente usado em programação de linguagem de montagem. 2. Gerenciamento de armazenamento dinâmico. • Um ponteiro pode ser usado para acessar uma posição na área onde o armazenamento é dinamicamente alocado, chamado de heap. • As variáveis dinamicamente alocadas a partir do heap são chamadas de variáveis dinâmicas do heap. Ponteiros e Referências • Ponteiros não são tipos estruturados, apesar de serem definidos usando um operador de tipo (* em C e C++ e access em Ada). • São diferentes de variáveis escalares porque são usados para referenciar alguma outra variável e não para armazenar dados de algum tipo. • Linguagens que fornecem um tipo ponteiro incluem duas operações fundamentais: atribuição e desreferenciamento. Ponteiros e Referências • Atribuição: – Modifica o valor de uma variável de ponteiro para algum endereço útil. – Deve existir um operador explícito ou um subprograma pré- definido para obter o endereço de uma variável. • Um ocorrência de ponteiro em uma expressão pode ser interpretada de duas maneiras: 1. Referência ao conteúdo da célula de memória a qual está vinculada. 2. Referência ao valor dentro da célula de memória apontado pela célula a qual a variável de ponteiro está vinculada (desreferenciar). Ponteiros e Referências • O desreferenciamento de ponteiros pode ser tanto explícito quanto implícito. • No Fortran 95, é implícito, mas em outras linguagens contemporâneas, ocorre apenas quando explicitamente especificado. • Em C++, ele é explicitamente especificado pelo símbolo asterisco (*). Exemplo: j = *ptr; Ponteiros e Referências Ponteiros e Referências – Exemplo em C #include <stdio.h> intmain(){ //Parte 1 int aux; int *auxPtr; printf("PARTE 1\n"); printf("Mensagem 1: %d\n", aux); printf("Mensagem 2: %d\n", &aux); printf("Mensagem 3: %d\n", auxPtr); printf("Mensagem 4: %d\n", *auxPtr); return 0; } Ponteiros e Referências – Exemplo em C Ponteiros e Referências – Exemplo em C #include <stdio.h> intmain(){ //Parte 1: ... //printf("Mensagem 4: %d\n", *auxPtr); aux = 13; auxPtr = &aux; printf("\nPARTE 2\n"); printf("Mensagem 5: %d\n", aux); printf("Mensagem 6: %d\n", &aux); printf("Mensagem 7: %d\n", auxPtr); printf("Mensagem 8: %d\n", *auxPtr); return 0; } Ponteiros e Referências – Exemplo em C Ponteiros e Referências • Quando os ponteiros apontam para registros, a sintaxe das referências para os campos desses registros varia entre as linguagens. • Linguagens que fornecem ponteiros para o gerenciamento de armazenamento dinâmico devem incluir uma operação explícita de alocação. • A alocação é algumas vezes especificada como um subprograma, como malloc em C. • Em C++, a alocação é feita com o operador new. Problemas com Ponteiros • Principal causa: algumas linguagens não permitem a liberação implícita da memória alocada. • Problemas comuns: – Ponteiros soltos – ponteiro que contém o endereço de uma variável dinâmica já liberada. – Variáveis dinâmicas perdidas – variável dinâmica alocada que não está mais acessível para os programas de usuário. Problemas com Ponteiros • Ponteiros soltos – A posição sendo apontada pode ter sido realocada para alguma outra variável dinâmica. – Se o ponteiro solto é usado para modificar a variável dinâmica, o valor da nova variável é destruído. – A liberação explícita de variáveis dinâmicas é a causa dos ponteiros soltos. Problemas com Ponteiros • Ponteiros soltos – Exemplo em C++: int *vetor1 = new int[100]; int *vetor2; vetor2 = vetor1; delete [] vetor1; //Agora, vetor2 é solto, porque o armazenamento para o qual ele estava apontando foi liberado Problemas com Ponteiros • Variáveis dinâmicas perdidas – Variáveis dinâmicas perdidas são frequentemente chamadas de lixo, pois não são mais úteis para seus propósitos gerais e não podem ser realocadas para algum novo uso no programa. – Usualmente são criadas pela seguinte sequência de operações: 1. O ponteiro p1 é configurado para uma variável dinâmica recém-alocada; 2. p1 é posteriormente configurado para apontar para outra variável dinâmica recém-criada. Tipos de Referência • Uma variável do tipo de referênciaé similar a um ponteiro, com uma diferença fundamental: – Um ponteiro se refere a um endereço em memória; – Uma referência indica um objeto ou um valor em memória. • Permitem liberação implícita da memória. • Todas as variáveis nas linguagens orientadas a objetos Smalltalk, Python, Ruby e Lua são referências. – Elas são sempre implicitamente desreferenciadas e seus valores diretos não podem ser acessados. Verificação de Tipos • A verificação de tipos é a atividade de garantir que os operandos de um operador são de tipos compatíveis. • Um tipo compatível ou é legal para o operador, ou é permitido a ele, dentro das regras da linguagem, ser implicitamente convertido pelo código gerado pelo compilador (ou pelo interpretador) para um tipo legal. • A conversão automática é chamada de coerção. Verificação de Tipos • Exemplo: – Se uma variável int e uma variável float são adicionadas em Java, o valor da variável inteira sofre uma coerção para float e uma adição de ponto flutuante é realizada. • Um erro de tipo é a aplicação de um operador a um operando de um tipo não apropriado. • Dois tipos de verificação: – Verificação de tipos estática; – Verificação de tipos dinâmica. Verificação de Tipos • Verificação de tipos estática: – Todas as vinculações de variáveis a tipos são estáticas; – Tempo de compilação; – Em C++ todas as variáveis são estaticamente vinculadas a tipos. • Verificação de tipos Dinâmica: – As vinculações de variáveis a tipos são dinâmicas; – Tempo de execução; – JavaScript e PHP só permitem verificação de tipos dinâmicas. Tipagem Forte • Uma linguagem de programação é fortemente tipada se erros de tipos são sempre detectados. • Requer que os tipos de todos os operandos possam ser determinados, em tempo de compilação ou em tempo de execução. • Habilidade de detectar usos incorretos de variáveis que resultam em erros de tipo. Tipagem Forte • Linguagem que não são fortemente tipadas: – Fortran 95: Equivalence; – C e C++: union. • Linguagens “quase” fortemente tipadas – Ada, Java e C#; – Não existem maneiras implícitas pelas quais os erros de tipos possam passar despercebidos; – Os tipos podem ser convertidos explicitamente, o que pode gerar um erro de tipos. • Linguagens fortemente tipadas: ML. Equivalência de Tipos • Regras de compatibilidade ditam os tipos de operandos aceitáveis para cada um dos operadores e especificam os possíveis tipos de erros da linguagem. • O tipo de um operando pode ser convertido implicitamente pelo compilador ou pelo sistema de tempo de execução para torná-lo aceitável ao operador. • Dois tipos são equivalentes se um operando de um em uma expressão é substituído por um de outro, sem coerção. Equivalência de Tipos • A equivalência de tipos é uma forma estrita de compatibilidade – compatibilidade sem coerção. • Existem duas abordagens para definir equivalência de tipos: – Por nome; – Por estrutura; Equivalência de Tipos • Equivalência de tipos por nome – Duas variáveis são equivalentes se são definidas na mesma declaração ou em declarações que usam o mesmo nome de tipo. – Fácil de implementar, mas é mais restritiva. – Uma variável cujo tipo é uma subfaixa dos inteiros não seria equivalente a uma variável do tipo inteiro. Por exemplo, em Ada: type IndiceTipo is 1..100; contador : Integer; indice : IndiceTipo; Equivalência de Tipos • Equivalência de tipos por estrutura – Duas variáveis são equivalentes se seus tipos têm estruturas idênticas. – Mais flexível, mas mais difícil de implementar. – Questões: dois registros são equivalentes se têm a mesma estrutura, mas nomes de campos diferentes? Dois vetores são equivalentes se têm o mesmo tipo de elemento, mas faixas de índices de 0..10 e 1..11? Equivalência de Tipos • Equivalência de tipos por estrutura – Outra dificuldade: não permite diferenciar tipos com a mesma estrutura. Exemplo em Ada: – Os tipos destas variáveis são equivalentes sob a equivalência de tipos por estrutura, permitindo que sejam misturados em expressões, o que é claramente indesejável nesse caso. type Celsius = Float; Fahrenheit = Float; Equivalência de Tipos • C usa tanto a equivalência de tipos por nome quanto por estrutura. – Tipos struct, enum e union criam um novo tipo que não é equivalente a nenhum outro. – Então, a equivalência de tipos por nome é usada para os tipos que representam estruturas, enumerações e uniões. – Tipos não escalares usam equivalência de tipos por estrutura. Tipos matrizes são equivalentes se tiverem componentes do mesmo tipo. – Se um tipo matriz tiver um tamanho constante, ele é equivalente ou a outras matrizes com mesmo tamanho ou àquelas sem um tamanho constante. Teoria e Tipos de Dados • A teoria de tipos é uma ampla área de estudo em matemática, lógica, ciência da computação e filosofia. • Em Ciência da Computação, existem dois ramos de teoria de tipos: – Prático – se preocupa com tipos de dados em linguagens de programação comerciais; – Abstrato – foca principalmente em cálculo lambda tipado, uma área de pesquisa intensa por parte de cientistas da computação teóricos.
Compartilhar