Baixe o app para aproveitar ainda mais
Prévia do material em texto
Ciência da Computação Tecnologia em Análise e Desenvolvimento de Sistemas Estrutura de Dados I INF1014 - CCB1044 Ponteiros SUMÁRIO • Organização de Memória • Definição de Ponteiro • Declaração e Inicialização • Operadores de Endereços • Expressões com Ponteiros • Ponteiros e Vetores • Ponteiros e Matrizes • Ponteiros e Estruturas • Arranjos de Ponteiros • Ponteiros para Ponteiros PONTEIROS • Organização de Memória – a memória de um computador é formada por uma sequência de bytes e cada byte é composto por 8 bits – cada byte pode armazenar 28 = 256 valores inteiros diferentes, que variam entre 0 e 255 – cada byte na memória é identificado por um endereço numérico, qualquer que seja o seu conteúdo bit (binary digit) é a menor unidade de informação que pode ser armazenada em um computador 0000 0001 0001 1001 0101 1010 1111 0101 1011 0011 0x0022FF16 0x0022FF17 0x0022FF18 0x0022FF19 0x0022FF1A conteúdo endereço PONTEIROS • Organização de Memória – cada objeto que reside na memória do computador ocupa um certo número de bytes, que varia de acordo com o seu tipo, e tem um endereço – Exemplos • char: 1 byte • int: 2 bytes • float: 4 bytes • double: 8 bytes 0110 0001 0000 0011 1110 1000 0x0022FF14 0x0022FF15 conteúdo endereço 0000 0111 1101 0000 char c = ‘a’; int[] i = {1000, 2000}; o tamanho e a faixa desses tipos de dados variam de acordo com o tipo de processador e com a implementação do compilador C PONTEIROS • Definição de Ponteiro – é uma variável que contém um endereço de memória e esse endereço é, normalmente, a posição de outra variável na memória – uma variável aponta para outra se a primeira contém o endereço da segunda endereçamento de 32 bits (CPUs mais modernas utilizam 64 bits) 0000 0101 0010 1010 0000 0000 0x0022FF14 0x0022FF15 conteúdo endereço 0010 0010 1111 1111 int x = 1322; int *py = &y; 0001 1011 0x0022FF16 0x0022FF17 0x0022FF18 0x0022FF19 0x0022FF1A 0000 0000 0010 1010 0x0022FF1B 0x0022FF1C int y = 42; PONTEIROS • Definição de Ponteiro – há pelo menos 3 razões para o emprego de ponteiros: • fornecem meios para que uma função modifique seus argumentos, ou seja, permitem que a função retorne mais de um valor • dão suporte às rotinas de alocação dinâmica, ou seja, permitem reservar posições de memória conforme a necessidade (tempo de execução) e não na declaração das variáveis (tempo de compilação) • aumentam a eficiência de certas rotinas – uma programação bem sucedida em C depende da compreensão e do uso correto de ponteiros PONTEIROS • Declaração e Inicialização – forma geral para a declaração de uma variável ponteiro – após a declaração o que se tem é um espaço de memória reservado para o armazenamento de endereços e o valor inicial é indefinido – assim como as variáveis tradicionais, os ponteiros devem ser inicializados antes de serem usados – um ponteiro pode ser inicializado com um endereço ou com o valor NULL (constante definida em stdio.h) tipo : qualquer tipo válido em C; nome : identificador da variável; tipo *nome; (ex_01) 0x0022FF14 0x0022FF15 conteúdo endereço int *px; 0x0022FF16 0x0022FF17 0x0022FF18 0x0022FF19 #include <stdio.h> void main (void) { int *px = NULL; px = 0x0022FF19; } (ex_02) PONTEIROS • Operadores de Endereço – & devolve o endereço de memória do seu operando – * devolve o valor do seu operando (ex_03) #include <stdio.h> int main (void) { int *pt_int = NULL; int ivalor = 555; pt_int = &ivalor; *pt_int = 333; printf ("Endereco = %d\n", pt_int); printf ("Conteudo = %d\n", *pt_int); } PONTEIROS • Operadores de Endereço int *a, *b; int c = 4, d = 2; a = &c; b = &d; *b = 8; *a = *b; *a = 1; b = a; *b = 0; PONTEIROS • Operadores de Endereço int *a, *b; int c = 4, d = 2; a = &c; // a apontará para c b = &d; // b apontará para d *b = 8; // altera o valor de d *a = *b; // copia o valor de d (apontado por b) // para c (apontado por a) *a = 1; // altera o valor da variável c b = a; // b aponta para o mesmo lugar que a, // ou seja, para c *b = 0; // altero o valor de c PONTEIROS • Expressões com Ponteiros – Atribuição • um ponteiro declarado como sendo de um tipo deve sempre apontar para variáveis deste mesmo tipo (ex_04) é possível atribuir qualquer endereço a uma variável ponteiro, porém, se for atribuído o endereço de uma variável que é de um tipo diferente do tipo do ponteiro, o programa não funcionará corretamente (ex_05) #include <stdio.h> int main (void) { int *pt_int = NULL; float fvalor = 2.5; pt_int = &fvalor; printf ("Conteudo fvalor = %f\n", fvalor); printf ("Conteudo *pt_int = %d\n", *pt_int); } Resultado: Conteudo fvalor = 2.500000 Conteudo *pt_int = 1075838976 Funcionamento incorreto. Conteúdo *pt_int não corrresponde ao valor atribuido a fvalor. PONTEIROS • Expressões com Ponteiros – Incremento e Decremento • ++ o ponteiro armazena o endereço de memória do elemento seguinte do seu tipo base • -- o ponteiro armazena o endereço de memória do elemento anterior do seu tipo base • forma geral para o incremento e o decremento de ponteiros (ex_06) nome: identificador da variável ponteiro; nome++; nome--; #include <stdio.h> int main (void) { int *pt_int; int ivalor = 555; char *pt_char; char cvalor = 'S'; pt_int = &ivalor; pt_char = &cvalor; printf ("pt_int = %d\t pt_char = %d\t \n", pt_int, pt_char); pt_int++; //somando uma unidade (4 bytes) na memória pt_char++; //somando uma unidade (1 byte) na memória printf ("pt_int = %d\t pt_char = %d\t \n", pt_int, pt_char); } Resultado: pt_int = 2293524 pt_char = 2293523 pt_int = 2293528 pt_char = 2293524 PONTEIROS • Expressões com Ponteiros – Adição e Subtração de Inteiros • ponteiros admitem a adição e a subtração de qualquer valor inteiro (ex_07) nome: identificador da variável ponteiro; valor: constante ou variável do tipo int; nome = nome + valor; nome = nome – valor; apenas a adição e a subtração podem ser usadas com ponteiros toda aritmética de ponteiros é relativa a seu tipo base char *pt_ch = 0xE0; int *pt_i = 0xE0; 0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 pt_ch pt_ch+1 pt_ch+2 pt_ch+3 pt_ch+4 pt_ch+5 pt_i pt_i+1 pt_i+2 Por simplificação, o exemplo usa endereçamento de 8 bits e inteiros de 2 bytes PONTEIROS • Expressões com Ponteiros – Comparação • é possível comparar ponteiros em uma expressão relacional, mas a comparação é restrita a ponteiros do mesmo tipo • forma geral para a comparação de ponteiros – Não é válido: • somar ponteiros (pi + pf) • multiplicar ou dividir ponteiros (pi*pf, pi/pf) • operar ponteiros com double ou float (pi ± 2.0) nomei : identificador da variável ponteiro i; op : operador relacional (<, <=, >, >=, ==, !=);if (nome1 op nome2) ... PONTEIROS • Expressões com Ponteiros – exemplo (ex_08) #include <stdio.h> #include <stdlib.h> #define SIZE 50 void push(int i); int pop(void); int *tos, *p1, stack[SIZE]; int main(void) { int value; tos = stack; //tos mantem end inicial p1 = stack; // p1 aponta para topo do { printf("Entre com o valor: "); scanf("%d", &value); if(value!=0) push(value); else printf("valor do topo‚ %d\n", pop()); } while(value!=-1); } void push(int i) { p1++; if(p1 ==(tos+SIZE)) { printf("estouro da pilha"); exit(1); } *p1 = i; } int pop(void) { if(p1 == tos) { printf("pilha vazia"); exit(1); } p1--; return *(p1+1); } PONTEIROS • Ponteiros e Vetores – Existe uma associação forte entre vetores e ponteiros, pois se existe a declaração: int v[10]; o símbolo v, que representa o vetor, é uma constante que representa seu endereço inicial, isto é, v, sem indexação, é um ponteiro constante que aponta para o primeiro elemento do vetor. – sendo uma constante, o nome de um vetor não pode ter o seu valor alterado; são inválidas as operações abaixo. (ex_09) int main(){ int list[5], i; // erro : o ponteiro list não pode ser modificado list = &i; // erro: o ponteiro list nao pode ser incrementado list++; } PONTEIROS • Ponteiros e Vetores – o conteúdo de um vetor pode ser acessado por meio do operador * e de aritmética de ponteiros – por exemplo, se p representa o ponteiro para um inteiro, p+1 representa um ponteiro para o próximo inteiro armazenado na memória. – portanto, escrever &v[i] é equivalente a escrever (v+i). E escrever v[i] é equivalente a escrever *(v+i). 1 int main(void) 2 { 3 int v[2]; 4 int *p; 5 p = v; 6 p = p + 1; 7 *p = 5; 8 return 0; 9 } Por simplificação, o exemplo usa endereçamento de 8 bits e inteiros de 2 bytes v[0] v[1] v p 00000000 00000000 00000000 conteúdo em 5 00000000 0xE0 0xE0 0xE0 endereço 0xE1 0xE2 0xE3 0xE5 0xE4 00000000 00000000 00000000 conteúdo em 6 00000000 0xE0 0xE2 00000000 00000000 00000101 conteúdo em 7 00000000 0xE0 0xE2 (ex_10) PONTEIROS • Ponteiros e Vetores – o conteúdo de um vetor também pode ser acessado por meio de indexação de ponteiro – há uma diferença importante entre declarar um conjunto de dados como vetor ou como ponteiro • vetor: o compilador reserva automaticamente um bloco de memória para o armazenamento do arranjo • ponteiro: o compilador aloca um ponteiro para apontar para a memória, sem que o espaço seja reservado int main(void) { int v[10]; int *p; p = v; p[5] = 100; return 0; } PONTEIROS • Ponteiros e Matrizes – assim como em um vetor, o conteúdo de um arranjo multidimensional (matriz) também pode ser acessado por meio do operador * – é necessário mapear o espaço multidimensional em apenas uma dimensão – em matrizes bidimensionais é necessário mapear o endereço de cada elemento, indexado por um par [i] [j], em um endereço linear – forma geral para o mapeamento (ex_11) nome: identificador do ponteiro; COL: número de colunas da matriz nome + (i * COL + j) 00 01 02 10 11 12 00 01 02 10 11 12 int M[2][3] linha 0 linha 1 PONTEIROS • Ponteiros e Matrizes – lembre-se que ponteiros podem ser utilizados para acessar elementos de um vetor usando indexação de ponteiro – fazendo: p = M[2] ; //equivale a p = &M[2][0] então, p[0] é o elemento M[2][0], p[1] é o elemento M[2][1],e assim por diante. – mas a construção p[i][j] não é válida e a atribuição abaixo não é correta, pois a linguagem C permite que um ponteiro seja utilizado apenas com uma dimensão p = M; //incorreto int M[3][4]; //inicializa os elementos da linha i de M com o conteudo c int init(int i, int c) { int *p; int j; p = M[i]; // p aponta para o 1o elemento da linha i de M; // equivale a fazer p = &M[i][0] for (j=0; j<4; j++) p[j] = c; // equivale a fazer M[i][j] = c } PONTEIROS • Ponteiros e Matrizes – uma matriz também não pode ser considerada um ponteiro para ponteiro, também devido a sua estrutura particular onde cada linha é seguida imediatamente por outra. – assim, a atribuição no pedaço de código abaixo também não é válida. int **p ; int M[3][4]; p = M ; //incorreto PONTEIROS • Ponteiros e Matrizes – a principal vantagem de se utilizar o endereçamento linear com ponteiro para varrer uma matriz é que essa é uma forma mais eficiente do que o endereçamento indexado por um par [i] [j]. – exemplo – cada vez que se faz matrx[i][j], o programa tem que calcular o deslocamento para adicionar ao endereço inicial , totalizando 2500 cálculos. – no segundo programa, o único cálculo que deve ser feito é o de um incremento de ponteiro. // Com acesso indexado void main () { float matrx[50][50]; int i,j; for (i=0;i<50;i++) for (j=0;j<50;j++) matrx[i][j]=0.0; } //Com endereçamento linear void main () { float matrx [50][50]; float *p; int count; p = matrx[0]; for (count=0;count<2500;count++) { *p=0.0; p++; } } PONTEIROS • Ponteiros e Estruturas – para definir ponteiros para estruturas a declaração é similar a declaração de um ponteiro normal – ponteiros para estruturas são úteis quando é necessário passa-las para funções – ao passar apenas o ponteiro, economiza-se tempo e memória, pois: • se evita copiar os dados que compõem a estrutura um por um; • não é necessário gastar o tempo de empilhar e desempilhar todos os elementos da estrutura no processo de passagem para a função. – para acessar elementos da estrutura apontada por um ponteiro usa-se o chamado operador seta (->) struct aluno { char nome [40]; int ano_entrada ; float n1 , n2 , media; } * maria; printf ("A media vale %.1 f", maria -> media ); PONTEIROS • Arranjos de Ponteiros – ponteiros podem ser organizados em arranjos como qualquer outro tipo de dado (ex_12) tipo *nome[]; tipo: qualquer tipo válido em C; nome: identificador da variável; #include <stdio.h> #include <stdlib .h> #define LINHAS 5 #define COLUNAS 10 int main (void) { char *linha[LINHAS]; char cadeia[COLUNAS]; int i; for (i = 0; i < LINHAS ; i++){ linha[i] = cadeia; //todas apontam para o mesmo string } for (i = 0; i < LINHAS ; i++){ printf ("Entre com a linha %d.\n", i); gets (linha[i]); //cadeia será sobrescrita } for (i = 0; i < LINHAS ; i++){ printf (" Linha %d %s.\n", i, linha[i]); } return 0; endereço endereço PONTEIROS • Ponteiros para Ponteiros – um ponteiro pode apontar para outro que aponta para o valor final – forma geral para a declaração de um ponteiro para ponteiro tipo **nome; tipo: qualquer tipo válido em C; nome: identificador da variável; ponteiro variável valor endereço valor ponteiro ponteiro variável (ex_13) ponteiro simples ponteiro para ponteiroponteiros podem apontar para ponteiros indefinidamente, mas raramente é necessário mais de um ponteiro para um ponteiro FIM
Compartilhar