Estrutura de dados 1 Ponteiros S Ponteiros S Um ponteiro é um endereço de memória S O valor de um ponteiro indica onde uma variável está armazenada S Um ponteiro proporciona um modo de acesso a uma variável sem precisar referenciá-la. Ponteiros S Os ponteiros são usados para: S Manipular elementos de matrizes; S Receber argumentos em funções que necessitem modificar o argumento original; S Passar strings de uma função para outra; S Criar estruturas de dados complexas, como listas encadeadas e árvores binárias, onde um item contém referências a outro; S Alocar e desalocar memória do sistema. Ponteiros S Toda variável ocupa uma localização na memória S o seu endereço é o endereço do primeiro byte ocupado por ela. S O endereço de memória é a referência usada para localizar as variáveis na memória. S Uma variável aponta para outra variável quando a primeira contém o endereço da segunda. &x = 0x7fff5a85eb58 x= 4 &y = 0x7fff5a85eb54 y= 7 px = 0x7fff5a85eb58 *px= 4 py = 0x7fff5a85eb54 *py= 7 Operador de endereço & S O operador de endereços (&) S é um operador unário e S seu operando deve ser o nome de uma variável, e S o resultado é seu endereço. Ex: #include <iostream.h> using namespace std; void main() { int x, y; cout << "\n" << &i; cout << "\n" << &j; } &x = 0x7fff5a85eb58 x= 4 &y = 0x7fff5a85eb54 y= 7 Operador de endereço & e o operador indireto * S O operador << imprime & endereços em hexadecimal precedidos pelo prefixo 0x. S O operador indireto (*) é S um operador unário S Seu operando é um endereço ou ponteiro e S resulta no conteúdo da variável localizada no endereço (ponteiro) do operando. S Os ponteiros oferecem uma maneira alternativa de passagem de parâmetros para uma função. Ponteiros &x = 0x7fff5a85eb58 x= 4 &y = 0x7fff5a85eb54 y= 7 #include <iostream.h> using namespace std; px = 0x7fff5a85eb58 *px= 4 void main( ) { py = 0x7fff5a85eb54 *py= 7 int x=4, y=7; cout << "\n&x = " << &x << "\t x= " << x; cout << "\n&y = " << &y << "\t y= " << y; int *px, *py; px = & x; py = & y; cout << "\n\npx = " << px << "\t *px= " << *px; cout << "\n\npy = " << py << "\t *py= << *py; } Ponteiros S A instrução “int *px, *py;” S declara ponteiros para variáveis int. S Quando um ponteiro não é inicializado na instrução de sua declaração, o compilador inicializa-o com o endereço zero (NULL) (não válido). S Um ponteiro pode ser inicializado no momento de sua declaração. Ex: int *px = &x; Operações com ponteiros S As operações com ponteiros só podem ser realizadas entre ponteiros de mesmo tipo. S Incrementar um ponteiro acarreta sua movimentação para o próximo endereço da memória de uma variável do mesmo tipo. S A diferença entre dois ponteiros representa um número de tipo apontado por eles. S Se py tem valor 4000 e px, 3998 a diferença entre eles é 1. Considerando px e py como inteiros. Operações com ponteiros S Ao declarar um ponteiro, o compilador deve conhecer o tipo de variável apontada para executar corretamente as operações aritméticas. S S S int *pi; double *pd; float *pf; S O tipo declarado é entendido como o tipo de variável apontada. S Ao somar 1 a pi, soma-se 4 bytes (1 int) S Ao somar 1 a pd, soma-se 8 bytes (1 double) &i = 0x7fff5a2a9b58 i= 4 &d = 0x7fff5a2a9b50 d= 7 pi = 0x7fff5a2a9b58 *pi= 4 pd = 0x7fff5a2a9b50 *pd= 7 pi + 1 = 0x7fff5a2a9b5c pd + 1 = 0x7fff5a2a9b58 Passando argumentos por referências com ponteiros S Para usar ponteiros como argumentos há duas etapas: S A função chamadora passa endereços usando o operador de endereços. S Estes endereços são de variáveis da função chamadora onde deseja-se que a função coloque novos valores. S A função chamada deve criar variáveis para armazenar os endereços enviados pela chamadora. S Estas variáveis são ponteiros. Passando argumentos por referências com ponteiros #include <iostream> using namespace std; void reajusta20(float *p, float *r); int main( ) { float preco, reajuste; do { cout << "\n\nInsira o preco atual:\n"; cin >> preco; reajusta20(&preco, &reajuste); cout << "\nPreco novo= "<< preco << "\nAumento= " << reajuste; } while (preco != 0.0); } void reajusta20(float *p, float *r) { *r= *p * 0.2; *p *= 1.2; } Atenção, função altera direto variável preco e reajuste Ponteiros e Matrizes S O compilador transforma matrizes em ponteiros na compilação. S O nome de uma matriz representa seu endereço de memória que é o endereço do primeiro elemento da matriz Ponteiros e Matrizes #include <iostream> #include <iostream> using namespace std; using namespace std; void main( ) void main( ) { { int M[5]={92,81,70,69,58}; int M[5]={92,81,70,69,58}; for (int i=0; i<5; i++) for (int i=0; i<5; i++) cout << “\n” << M[i]; cout << “\n” << *(M+i); } } • M é um ponteiro int e aponta para M[0] • Ao somar 1 a M obtém-se M[1] • M + i é equivalente a &M[i] • *(M+i) é equivalente a M[i] Passando matrizes como argumentos para funções #include <iostream> using namespace std; int media (int *lista, int tamanho); int main( ) { int notas [20],i; for (i=0; i<20; i++) { cout << "\nDigite a nota do aluno " << (i+1) << ": "; cin >> *(notas+i); if (*(notas+i) < 0) break; } int m = media (notas, i); cout << "\n\nMedia das notas: " << m; } int media (int *lista, int tamanho) { int m=0; for (int i=0; i < tamanho; i++) m += *lista++; return (m/tamanho); } Alocação de memória S A alocação estática de memória ocorre em tempo de compilação, ou seja, no momento em que se define uma variável ou estrutura S é necessário definir seu tipo e tamanho. S A alocação dinâmica de memória ocorre em tempo de execução, no momento em que a variável ou estrutura precisar ser utilizada, S sua memória será reservada e será liberada se a memória não for mais necessária. S Em C e C++ é possível criar matrizes e vetores dinâmicos através de ponteiros. Área de alocação dinâmica: heap S A área de alocação dinâmica (heap) S toda a memória que ainda não está alocada. S Em C++ há dois operadores para a alocação ou liberação da memória dinâmica: new e delete. S Pode-se solicitar memória sempre que necessário. S A operador new obtém memória do sistema operacional e retorna um ponteiro para o primeiro byte do bloco de memória alocado. S S S S sintaxe: new tipo; sintaxe: new tipo [numero_de_elementos]; Quando alocamos memória dinamicamente é recomendado que ao término da execução do programa essa memória seja liberada. Para isto existe o operador delete. S Sintaxe: delete ponteiro; S Sintaxe: delete [ ] ponteiro; Uma vez alocada, a memória só é liberada pela função free(). Área de alocação dinâmica #include <iostream> #include <new> using namespace std; int main () { int i,n; int * p; cout << "Quantos numeros voce quer? "; cin >> i; p= new (nothrow) int[i]; if (p == nullptr) cout << "** Erro: Memoria Insuficiente "; else { for (n=0; n<i; n++) { cout << "Entre um numero: "; cin >> p[n]; } cout << "Voce digitou: "; for (n=0; n<i; n++) cout << p[n] << ", "; delete[] p; } return 0; } Área de alocação dinâmica: heap S Em C há duas funções para a alocação ou liberação da memória dinâmica: malloc() e free(). S Pode-se solicitar memória sempre que necessário. S O operador new obtém memória do sistema operacional e retorna um ponteiro para o primeiro byte do bloco de memória alocado. S Uma vez alocada, a memória só é liberada pelo operador delete.