Baixe o app para aproveitar ainda mais
Prévia do material em texto
Plano de Aula: Alocação Dinâmica de Memória (Parte I) ESTRUTURA DE DADOS - CCT0637 Título Alocação Dinâmica de Memória (Parte I) Número de Aulas por Semana Número de Semana de Aula 4 Tema Alocação Dinâmica de Memória (Parte I) Objetivos Possibilitar ao aluno: - Reconhecer os conceitos fundamentais relacionados à Alocação Dinâmica de Memória - Analisar como acontece a Alocação Dinâmica de Memória em C++ - Aplicar o tipo de dado Ponteiro e os seus operadores associados: endereço, indireção, seta no desenvolvimento de algoritmos - Desenvolver, no laboratório de informática, os conceitos sobre Ponteiros e suas operações associadas por meio de exercícios guiados Estrutura do Conteúdo CONTEÚDOS: Alocação Dinâmica de Memória Fundamentos Aplicação em C++: Ponteiros Operador de endereço (&), operador de indireção (*) e operador seta (->) RESUMO DE CONCEITOS: Definições Ponteiros são fundamentais para a programação bem sucedida em C++: Passagem de parâmetros por referência; Alocação dinâmica de memória; Aumentar a eficiência de certar rotinas. Essencialmente, um ponteiro nada mais é do que uma variável que ao invés de conter um valor, contém um endereço de memória. O conceito de utilizar espaço em memória não é em absoluto novo. De fato, linguagens de máquina utilizam tal conceito todo o tempo. Exemplo 1 - mnemônico (Intel 80C51) que executa a soma do conteúdo da RAM interna apontada por Ri ao Acumulador: ADD A, @RI Declaração de Ponteiros <tipo> *<nome_variavel> Exemplo 2 - exemplo da declaração de diversos ponteiros: int *iptr; float *fptr; char *cptr; Como um ponteiro armazena um endereço de memória e não um valor específico, pode parecer estranho a primeira vista que seja requerida a associação de um tipo ao se declaram um ponteiro. No entanto, muito comumente, deseja-se acessar o valor armazenado pela variável apontada por um ponteiro. Desta forma, o tipo do ponteiro serve para instruir o compilador na maneira pela qual o dado pontado será acessado. Essencialmente, o tipo de dados e a codificação do dado (como os bits serão interpretados, necessário por exemplo para tipos com base na notação de ponto flutuante). Operadores de Ponteiros "*" conteúdo / indireção: aplicado a um variável tipo ponteiro, retorna o conteúdo do endereço apontado pelo ponteiro; "&" endereço: aplicado a uma variável, retorna seu endereço de memória. Exemplo 3 - exemplo da utilização de ambos os operadores unários: int count; int *iptr; iptr = &count; cout << *iptr; Operador Seta Para começar e deixar mais claro definimos uma estrutura simples com dois campos. struct { int i; double f; } minha_estrutura; O passo seguinte é definir um ponteiro para essa estrutura. struct minha_estrutura *p_minha_estrutura; A partir do ponteiro podemos ter acesso a um campo da estrutura usando um seletor "->" (uma flecha). p_minha_estrutura-> i = 1; p_minha_estrutura-> f = 1.2; O mesmo resultado pode ser obtido da seguinte forma. (*p_minha_estrutura).i = 1; (*p_minha_estrutura).f = 1.2; Inicialização de Ponteiros Ponteiros são inicializados ao definirmos um endereço para o qual eles devem apontar. Ponteiros não inicializados são também chamados de "ponteiros selvagens" A não inicialização de ponteiros é um dos principais fatores para erros em tempo de execução em programas escritos na linguagem C++. Tais erros são difíceis de identificar e corrigir, porque o tempo decorrido entre o acesso ilegal à memória de um ponteiro e a efetiva ocorrência do erro é imprevisível. Uma maneira de garantir que um ponteiro declarado não apontará para um lugar qualquer da memória é atribuir o valor NULL para o mesmo. int *iptr = null; Ponteiros e Arrays Ponteiros são muito utilizados para a navegação de arrays, principalmente de arrays alocados de maneira dinâmica. A idéia geral é alocar o array usando ou o método estático ou o dinâmico e então criar uma variável do tipo ponteiro que apontará para a primeira posição do ponteiro. Utilizando aritmética de ponteiros (incremento, decremento, saltos) é possível percorrer o array sem ter que se preocupar com quantos bytes devem ser acessados, pois a tipagem do ponteiro cuidará deste detalhe. É importante ressaltar que a utilização de ponteiros para indexação de arrays apresenta um risco intrínseco. Aritmética de Ponteiros Uma das grandes vantagens decorrentes da utilização de ponteiros é a possibilidade de indexar posições de memória de maneira fácil e intuitiva. Tal endereçamento é alcançado a partir de operações aritméticas simples que tem como objetos variáveis do tipo ponteiro. Operandos unários: ++ incremento unitário; -- decremento unitário. Suponha que p é um ponteiro do tipo inteiro e suponha que o tipo inteiro na arquitetura de destino do exemplo sejam representados por 2 bytes cada. Considere o seguinte trecho de código: int vet[10]; int *iptr; iptr = vet; iptr++; Exemplo 4 - navegação de arrays usando ponteiros Assumindo que o array "vet" seja alocado pelo "loader" do sistema operacional iniciando na posição 00H, iptr erá apontar para a posição 00H inicialmente. Ao se executar a operação iptr++, o endereço apontado por iptr será incrementado em uma unidade, o que corresponderá, dado o fato de que inteiros são representados por dois bytes, ao endereço 02H. Comparação de Ponteiros O ponto importante a respeito da comparação de ponteiros a ser considerado diz respeito a semântica da comparação. Como ponteiros são variáveis que contém endereços de memória, ao compararmos ponteiros o que estamos fazendo realmente é verificando se dois endereços de memória são idênticos ou não. A primeira vista, tal tipo de comparação pode parecer de pouca importância, no entanto ela será de grande valor futuramente quando ponteiros forem utilizados para manipular estruturas de dados complexas tal como listas ligadas ou árvores. Podemos comparar se dois ponteiros são iguais menores ou maiores exatamente como se compararam valores de variáveis. int vet[7]; int *iptr1, *iptr2; iptr1 = &vet[0]; iptr2 = &vet[6]; iptr1 == iptr2; iptr1 < iptr2; iptr1 > iptr2; iptr1 <= iptr2; Exemplo 5 - comparação de ponteiros Programa Exemplo #include <iostream> #include <stdlib.h> using namespace std; int main() { //int count = 22; //int aux = 456; int *iptr = (int*) malloc(100*sizeof(int)); //int *idx = NULL; //idx = iptr; int i; for (i = 0; i < 100; i++) { iptr[i] = i * 10; //*idx = i * 10; //idx++; } for (i = 0; i < 10; i++) { cout << "Elemento " << i << " = " << iptr[i] << endl; } //iptr = &count; /* if (iptr != NULL) { cout << "Endereco: " << &count << " - Valor: " << count << endl; cout << "Ponteiro: " << iptr << " - Valor0 " << iptr[0] << " - Valor1 " << iptr[1] << endl; } else { cout << "Ponteiro Nulo" << endl; } */ return 0; } Alocação de vetores de Strings #include <iostream> #include <stdlib.h> using namespace std; #define SIZE 2 int main() { int i; // Vetor estatico de strings estaticas char vet1[SIZE][255] = { "Estatico-1", "Estatico-2" }; for (i = 0; i < SIZE; i++) { cout << vet1[0] << " - " << vet1[1] << endl; } // Vetor dinamico de strings dinamicas char **vet; vet = (char**) new char[SIZE]; for (i = 0; i < SIZE; i++) { vet[i] = new char[255];//malloc (255 * sizeof(char)); cout << vet[i] << "Dinamico-" << i << endl; } for (i = 0; i < SIZE; i++) { cout << vet[i] << endl; } return 0; } Aplicação Prática Teórica SUGESTÃO DE EXERCÍCIOSPARA SEMANA 04 TEÓRICOS 1. Ano: 2010 / Banca: FCC / Órgão: TRE-AM / Prova: Técnico Judiciário - Programação de Sistemas Na linguagem C++, considere: I. O endereço armazenado em um ponteiro deve ser do mesmo tipo que o ponteiro (ex. um ponteiro para um int não pode armazenar o endereço de um float). II. Exceção à regra apontada em (I) é o ponteiro void. III. Não é possível chamar uma função segundo seu endereço, ainda que por meio de um ponteiro que armazena o endereço de início dessa função. Está correto o que se afirma em a) I, apenas. b) II, apenas. c) I e II, apenas. d) II e III, apenas. e) I, II e III. 1. Ano: 2004 / Banca: CESPE / Órgão: TRE-AL / Prova: Analista Judiciário - Tecnologia da Informação A atividade de programação requer conhecimento técnico de diversas formas de algoritmos e estruturas de controle e de dados. Acerca dos elementos técnicos da atividade de programação, julgue os itens a seguir. Um ponteiro definido como um dos elementos de um tipo estruturado de dados pode apontar para uma instância de dados desse mesmo tipo. ( ) Certo ( ) Errado 1. Ano: 2009 / Banca: CESPE / Órgão: TRE-GO / Prova: Técnico Judiciário - Programação de Sistemas Considerando as definições em linguagem C mostradas acima, assinale a opção correta. a) A utilização de (char*) é um cast, que converte um tipo de dados em outro. b) As variáveis px e pc apontam para posições de memória diferentes. c) É correto afirmar que px + 1 e (*pc) + 1 apontam para as mesmas posições de memória. d) Se px é um ponteiro para a variável x de tipo float, a expressão px + 1 se refere ao byte seguinte na memória, a partir do endereço de x. 1. Ano: 2010 / Banca: CESGRANRIO / Órgão: IBGE / Prova: Analista de Sistemas Para os recursos presentes na linguagem de programação C, são feitas as afirmativas abaixo. I - Permite acesso de baixo nível através da introdução de código Assembly no programa C. II - A passagem de parâmetros por referência para funções pode ser simulada através da utilização de ponteiros. III - O tipo de dados typedef são estruturas variáveis que permitem que dados relacionados sejam combinados e manipulados como um todo. Está(ão) correta(s) a(s) afirmativa(s) a) I, apenas. b) II, apenas. c) III, apenas. d) I e II, apenas. e) I, II e III. 1. Ano: 2014 / Banca: UNIRIO / Órgão: UNIRIO / Prova: Analista Tecnologia da Informação - Desenvolvimento de Sistemas Sobre as funções, é CORRETO afirmar que a) na passagem por valor, o parâmetro que vai ser passado na chamada da função deve ser uma variável, de tal forma que uma alteração de valor neste parâmetro também altera a variável correspondente. b) na passagem por referência, o parâmetro que vai ser passado na chamada da função deve ser uma variável, de tal forma que uma alteração de valor neste parâmetro também altera a variável correspondente. c) uma variável é dita global quando a sua passagem no momento da chamada de uma função se dá tanto por valor quanto por referência, enquanto que uma variável é dita local quando esta passagem se dá, apenas, por valor. d) na passagem por valor, o parâmetro passado na chamada da função é o ponteiro para a variável que contém o valor desejado. 1. Ano: 2008 / Banca: ESAF / Órgão: Prefeitura de Natal - RN / Prova: Auditor do Tesouro Municipal - Tecnologia da Informação Analise as seguintes afirmações relacionadas a conceitos básicos sobre Programação: I. Um procedimento é um conjunto de comandos para uma tarefa específica referenciada por um nome no algoritmo principal, retornando um determinado valor no seu próprio nome. II. Podem-se inserir módulos em um algoritmo. Para isso, pode- se utilizar "Procedimentos" ou "Funções". As ações das "Funções" e dos "Procedimentos" são hierarquicamente subordinadas a um módulo principal. III. Cada "Função" ou "Procedimento" pode utilizar constantes ou variáveis do módulo principal ou definir suas próprias constantes ou variáveis. IV. Uma variável global indica o endereço onde um valor é armazenado na memória do computador enquanto um ponteiro representa um valor numérico real. Indique a opção que contenha todas as afirmações verdadeiras. a) I e II. b) II e III. c) III e IV. d) I e III. e) II e IV. 1. Ano: 2006 / Banca: ESAF / Órgão: CGU / Prova: Analista de Finanças e Controle - Tecnologia da Informação Analise as seguintes afirmações relacionadas a conceitos básicos de Programação de Computadores. I. O escopo de uma variável de programa é a faixa de instruções na qual a variável é visível. Uma variável é visível em uma instrução se puder ser referenciada nessa instrução. II. Um registro é um agregado, possivelmente heterogêneo de elementos, cujos elementos individuais são identificados por nomes. III. Um array é um agregado heterogêneo de elementos de dados, cujo elemento individual é identificado por sua posição em relação ao primeiro. IV. Um tipo Ponteiro é aquele em que as variáveis têm uma faixa de valores que consiste em uma string ou coleção de caracteres e um valor especial denominado Null. Indique a opção que contenha todas as afirmações verdadeiras. a) I e II b) II e III c) III e IV d) I e III e) II e IV PRÁTICOS 1. Execute o programa abaixo passo a passo no compilador e registre em papel os endereços alocados para cada variável (inclusive os ponteiros). Observe o que acontece no decorrer do programa e no final. Tente entender o que o programa faz e explique porque acontece o erro de execução ao final. # include <stdio.h> int main () { int x, *p, *q, i, k=0; q = p = &x; for (i=1; i <= 3; i++) { scanf ("%d", p); p++; } p--; while (p >= q) { k = k + *p; p--; } printf ("%d", k); } Inverta a ordem de declaração das variáveis e observe o que acontece. 1. Uma das utilidades de um ponteiro é de permitir a uma subrotina retornar mais de um valor para suas computações (se comunicando com variáveis externas a subrotina). Codifique uma subrotina que receba um número inteiro representando um total de segundos e o endereço para duas variáveis (também inteiras) para guardar a quantida de minutos e de horas. A subrotina deve decompor o número de segundos recebidos em um total de horas e um total de minutos, retornando estes valores através dos endereços de variáveis recebidos como parâmetro. Codifique também uma função main que use a subrotina de conversão. 2. Codifique uma subrotina que receba um vetor de inteiros e o endereço de duas variáveis (também inteiras). O objetivo da subrotina é procurar neste vetor o menor e maior valor e armazená-los nas variáveis apontadas pelos endereços de memória recebidos.
Compartilhar