Universidade Federal de Uberlândia Faculdade de Computação Linguagem C: ponteiros e alocação dinâmica Prof. Renato Pimentel 1 Ponteiros 2 Prof. Renato Pimentel 1
Ponteiros: introdução Toda a informação sendo processada Memória primária do computador; A cada variável criada, é reservado um espaço de memória; Espaço é identificado pelo endereço do bit inicial. Identificador da variável Endereço de memória 3 Ponteiros: introdução A cada variável criada, é reservado um espaço de memória; Através do identificador (nome) da variável, o programador tem acesso ao seu conteúdo; E se quisermos armazenar o endereço de memória de um dado, ao invés de seu conteúdo? Identificador da variável Endereço de memória 4 Prof. Renato Pimentel 2
Ponteiros: introdução Ponteiro: variável que armazena endereços de memória. Permite ainda acessar dado ou variável sem referenciá-lo diretamente (acesso indireto). Utilidade: Quando uso do nome da variável é indesejável ou não permitido; Permite que uma função modifique os argumentos recebidos; Manipulação mais fácil de elementos de um array; Alocação (reserva) e desalocação de memória. Criação de estruturas de dados mais complexas, onde cada item deve conter referências para um outro. 5 Ponteiros: declaração A declaração de um ponteiro para um dado tipo de variável é feita da seguinte maneira: tipo_do_ponteiro *nome_do_ponteiro; Operador *: na declaração, informa que a variável sendo declarada armazenará um endereço de memória para o tipo de dado escolhido. Exemplo: int *p; // ponteiro para um dado do tipo inteiro 6 Prof. Renato Pimentel 3
Ponteiros: atribuição A simples declaração de um ponteiro não implica em uso imediato: É preciso, antes, atribuir um endereço ao mesmo! 7 Ponteiros: atribuição Após linha 5: Memória posição var. conteúdo 1201 a 10 1202 1203 *p???? 1204 8 Prof. Renato Pimentel 4
Ponteiros: atribuição Após linha 6: Memória posição var. conteúdo 1201 a 10 1202 1203 *p 1201 1204 9 Ponteiros: operadores * e & Operador *: Na declaração, informa que a variável sendo declarada armazenará um endereço de memória para o tipo de dado escolhido: ex.: int *x; Durante execução, indica conteúdo para onde o ponteiro aponta: ex.: int y = *x; Operador &: Endereço onde uma variável está guardada na memória: exs.: x = &y; scanf( %d, &y); 10 Prof. Renato Pimentel 5
Ponteiros: cuidado Erro!! Por quê? Variável p foi declarada, porém não foi inicializada!! 12 Prof. Renato Pimentel 6
Ponteiros: cuidado Um outro cuidado que é preciso tomar: o que estamos atribuindo ao ponteiro! 13 Ponteiros: cuidado Um outro cuidado que é preciso tomar: o que estamos atribuindo ao ponteiro! Saída do programa anterior: Conteudo apontado por p: 10 Conteudo apontado por p1: 10 Conteudo apontado por p: 1101004800 Conteudo apontado por p: 0.000000 Conteudo apontado por p: 20.000000 14 Prof. Renato Pimentel 7
Ponteiros: cuidado Um outro cuidado que é preciso tomar: o que estamos atribuindo ao ponteiro! Neste programa, o endereço de de uma variável do tipo float é atribuído a um ponteiro do tipo int. Saída do programa anterior: Conteudo apontado por p: 10 Acesso ao conteúdo somente possível através do uso de um modelador sobre o ponteiro (linha 13). Conteudo apontado por p1: 10 Conteudo apontado por p: 1101004800 Obs.: Note que é possível um ponteiro receber o endereço apontado por outro ponteiro, quando ambos são do mesmo tipo (vide linha 8). Conteudo ambos apontado são mesmo por tipo p: (vide 0.000000 linha 8). Conteudo apontado por p: 20.000000 15 Ponteiros: operações Operações aritméticas com ponteiros: Adição (operador +) e subtração (operador -): podemos somar ou subtrair valores inteiros de um endereço armazenado por um ponteiro (avanço ou retrocesso na memória principal, do endereço para onde ponteiro aponta). Útil quando se trabalha com arrays elementos adjacentes na memória. Operações relacionais: >, <, >=, <=, ==,!=: usados para saber se endereço apontado por um ponteiro está em uma certa posição à frente, atrás, à frente ou igual, etc. da posição de outro ponteiro. 16 Prof. Renato Pimentel 8
Ponteiros: operações Exemplos de operações aritméticas: int *p = 1500; // Cuidado: o que há nesta posição? p++; printf( p = %d, p); // p=1504 (avanço de 4 bytes) p = p-15; printf( p = %d, p); // p=1444 (retrocede 15*4 bytes) Exemplos de operações relacionais: int *p, *p1, x, y; p = &x; p1 = &y; if (p == p1) printf( ponteiros iguais\n ); else printf( ponteiros diferentes\n ); 17 Ponteiros e arrays Arrays: agrupamentos adjacentes de dados de um mesmo tipo na memória. Na declaração: é feito pedido para que computador reserve certa quantidade de memória, para armazenar os elementos do array de forma sequencial. Computador devolve um ponteiro que aponta para o começo da sequência de bytes. Nome do array: ponteiro que aponta para o primeiro elemento do mesmo (nome do endereço sem índice guarda endereço para o começo do array na memória). 18 Prof. Renato Pimentel 9
Ponteiros e arrays Exemplo: int vet[5] = {1,2,3,4,5}, i; int *p = vet; // p aponta p/ 1o elemento de vet for (i=0; i<5; i++) printf( %d\n, p[i]); for (i=0; i<5; i++) printf( %d\n, *(p+i)); Os dois últimos comandos acima são equivalentes: p[indice] equivale a *(p+indice) (bem como p = vet equivale a p = &vet[0]) No caso de arrays multidimensionais (já vimos que são armazenados linearmente na memória), a situação é a mesma: p[i][j] equivale a *(p + i x (número de colunas) + j) 19 Alocação dinâmica de memória 20 Prof. Renato Pimentel 10
Problema Vetores e matrizes são úteis para manipular conjuntos de dados; No entanto, representam variáveis estáticas em um programa: Não é possível determinar ou modificar seu tamanho em tempo de execução; Em diversas situações reais, é necessário que essas estruturas sejam dinâmicas. 21 Problema Imagine a seguinte situação: precisamos construir um programa para processar os valores dos salários de uma empresa com 10000 funcionários: float salarios[10000]; Se a empresa tem menos de 10000 funcionários: array será exemplo de desperdício de memória; Se a empresa tiver mais de 10000 funcionários: array insuficiente para armazenamento e manipulação dos salários de todos os funcionários. Solução: alocação dinâmica! 22 Prof. Renato Pimentel 11
Alocação dinâmica Processo de alocar (reservar) memória para um programa em tempo de execução; Quantidade de memória é alocada sob demanda, ou seja, quando o programa precisa: Menos desperdício de memória; Espaço é reservado até liberação explícita: Depois de liberado, estará disponibilizado para outros usos e não pode mais ser acessado; Espaço alocado e não liberado explicitamente é automaticamente liberado ao final da execução. 23 Alocação dinâmica: funções Em linguagem C ANSI, 4 funções são utilizadas, disponíveis na biblioteca stdlib.h: malloc( ) calloc( ) realloc( ) free( ) As duas primeiras alocam memória, a terceira realoca, e a quarta libera memória. Uma outra função, auxiliar, é também definida: sizeof( ) 24 Prof. Renato Pimentel 12
Alocação dinâmica: sizeof( ) Função sizeof( ): retorna o tamanho em bytes de variáveis ou tipos. Pode ser usada de duas formas: sizeof nome_da_variavel sizeof (nome_do_tipo) Exemplo: int x; printf( Tamanho de float: %d\n,sizeof(float)); printf( Tamanho de x: %d\n, sizeof x); 25 Alocação dinâmica: malloc( ) Aloca memória durante a execução do programa. Faz pedido de memória ao computador Retorna ponteiro com endereço do início do espaço de memória alocado. Sintaxe: void *malloc (unsigned int num); Recebe um parâmetro de entrada num: tamanho do espaço a ser alocado, em bytes. Retorna endereço do início ou NULL em caso de erro. Retorno do tipo ponteiro genérico (void *): não se sabe o tipo de dado que será usado, que será passado via cast. 26 Prof. Renato Pimentel 13
Alocação dinâmica: malloc( ) Exemplo: int *p, i; p = (int *) malloc(5*sizeof(int)); for (i=0; i<5; i++) scanf( %d,&p[i]); Alocamos um array contendo 5 posições de inteiros (20 bytes sizeof(int) = 4); malloc( ) retorna ponteiro genérico, convertido para ponteiro para inteiro via cast (int *); Ponteiro p passa a ser tratado como array: p[i]. 27 Alocação dinâmica: malloc( ) Verificando se alocação foi corretamente realizada: int *p, i; p = (int *) malloc(5*sizeof(int)); if (p == NULL) { printf( Erro: memoria insuficiente!\n ); system( pause ); return 1; }... 28 Prof. Renato Pimentel 14
Alocação dinâmica: malloc( ) Alocando espaço para arrays multidimensionais. Ponteiro para ponteiro: int **A // matriz m x n; A = (int **) malloc(m * sizeof (int *)); for (i = 0; i < m; ++i) A[i] = (int *) malloc(n * sizeof (int)); Outra forma mais simples seria fazer A = (int *) malloc(m * n * sizeof (int)); 29 Alocação dinâmica: calloc( ) Aloca memória durante a execução do programa. Faz pedido de memória ao computador Retorna ponteiro com endereço do início do espaço de memória alocado. Sintaxe: void *calloc (unsigned int num, unsigned int size); Recebe 2 parâmetros de entrada num: número de elementos do array a serem alocados. size: tamanho em bytes de cada elemento. A saída é idêntica ao malloc( ). Única diferença entre as funções: no calloc( ) passamos a quantidade de dados e o tamanho de cada um como 2 parâmetros diferentes. 30 Prof. Renato Pimentel 15
Alocação dinâmica: calloc( ) Alterando exemplo anterior: int *p, i; p = (int *) calloc(5, sizeof(int)); if (p == NULL) { printf( Erro: memoria insuficiente!\n ); system( pause ); return 1; } for (i=0; i<5; i++) scanf( %d,&p[i]); 31 Alocação dinâmica: realloc( ) Serve para alocar memória ou realocar blocos previamente alocados pelas funções malloc( ), calloc( ) ou a própria realloc( ). Sintaxe: void *realloc (void *p, unsigned int num); Recebe 2 parâmetros de entrada p: ponteiro para o bloco previamente alocado. num: número de elementos do array a serem alocados. A saída é idêntica ao malloc( )e calloc( ). 32 Prof. Renato Pimentel 16
Alocação dinâmica: realloc( ) Observações: Na realocação, é possível aumentar ou reduzir espaço de memória alocado. Pode haver necessidade, em caso de aumento, de deslocar o bloco de memória para outra posição Neste caso, dados são copiados não há perda de informação. Conteúdo dos novos elementos em caso de aumento é indefinido (não é inicializado) Isso vale também para malloc( ) e calloc( ). Se num for igual a zero, memória apontada por *p é liberada, como comando free( ) a ser visto. 33 Alocação dinâmica: realloc( ) Exemplo: int *p, i; p = (int *) calloc(5, sizeof(int)); for (i=0; i<5; i++) p[i]=i+1; p = realloc(p, 3*sizeof(int)); for (i=0; i<3; i++) printf( %d\n, p[i]); p = realloc(p, 10*sizeof(int)); for (i=0; i<10; i++) printf( %d\n, p[i]); 34 Prof. Renato Pimentel 17
Alocação dinâmica: free( ) Variáveis alocadas dinamicamente não são automaticamente liberadas: É necessário liberar a memória quando a mesma não for mais utilizada Para liberar um bloco de memória, usamos free( ). Sintaxe: void free (void *p); Recebe 1 parâmetro de entrada p: ponteiro para o bloco previamente alocado a ser liberado. 35 Alocação dinâmica:free( ) Observações: Programa sabe quantos bytes precisam ler liberados. Informação do tamanho de cada espaço é armazenada numa tabela de alocação interna. Não convém deixar ponteiro solto no programa (após execução do comando, ponteiro em questão continua apontando para endereço do início do bloco). Ideal é fazer ponteiro apontar para NULL após. 36 Prof. Renato Pimentel 18
Alocação dinâmica: realloc( ) Exemplo: int *p, i; p = (int *) calloc(50, sizeof(int)); for (i=0; i<50; i++) p[i]=i+1; for (i=0; i<50; i++) printf( %d\n, p[i]); free (p); system( pause );... 37 Alocação dinâmica: realloc( ) Seguindo a recomendação do ponteiro apontar para NULL: int *p, i; p = (int *) calloc(50, sizeof(int)); for (i=0; i<50; i++) p[i]=i+1; for (i=0; i<50; i++) printf( %d\n, p[i]); free (p); p = NULL; system( pause );... 38 Prof. Renato Pimentel 19
Exercícios 1. Escreva um programa em linguagem C que solicita ao usuário a quantidade de alunos de uma turma e aloca um vetor de notas (números reais). Depois de ler as notas, mostre na tela todas as notas separadas por um espaço, e por fim a média aritmética. 2. Desenvolva um programa que calcule a soma de duas matrizes MxN de números reais (double). A implementação deste programa deve considerar as dimensões fornecidas pelo usuário. 39 Exercícios 3. Faça um programa que crie um vetor de inteiros com 1 posição, depois peça para o usuário entrar valores até que digite -1. Depois percorra o vetor e mostre o maior dos elementos digitados. 40 Prof. Renato Pimentel 20
Referências BACKES, A. Linguagem C: completa e descomplicada. Rio de Janeiro: Elsevier, 2013. PAIVA, J. G. S. Notas de aula de algoritmos e programação de computadores. 41 Prof. Renato Pimentel 21