prof. ricardo santos [email protected] ponteiros
TRANSCRIPT
Prof. Ricardo [email protected]
PONTEIROS
Ponteiros
Um ponteiro é uma variável que contém um endereço de memória. Este endereço é a posição de outra variável na memória. Se uma variável contém o endereço de uma outra, dizemos que a primeira aponta para a segunda.
Declaração: tipo_base *nome;
Exemplo: int *p;
Onde tipo_base é qualquer tipo válido em C e nome é o nome da variável ponteiro. O tipo_base define que tipo de variáveis o ponteiro pode apontar.
Endereçona Memória
Conteúdo
1000 1003
1001
1002
1003
1004
1005
1006
Ponteiros
Uma variável ponteiro ocupa duas partes na memória, chamadas de parte de posição e parte de valor.
int *p; (1)*p = 10; (2)
Memória
null
p
Memória
p
10
Parte de Posição
Parte de Valor
Parte de Posição
Efeito da instrução 1 Efeito da instrução 2
Ponteiros
Na instrução 1 a variável p só tem parte de posição e seu conteúdo é null pois ainda não aponta para nenhum endereço.Na instrução 2 é que p terá a parte de valor e a parte de posição terá um endereço (diferente de null).
int *p; (1)*p = 10; (2)
Memória
null
p
Memória
p
10
Parte de Posição
Parte de Valor
Parte de Posição
Efeito da instrução 1 Efeito da instrução 2
Operadores de Ponteiros
Para selecionar cada uma das partes de uma variável ponteiro p, usa-se:
p para especificar a parte de posição da variável p. Pode conter um endereço de memória ou null se não estiver apontado para um valor.
*p para especificar a parte de valor da variável p.
Observação: O operador & serve para especificar o endereço na memória da variável que o segue.
Exemplificando
Suponha que a variável contador usa a posição de memória 2000 para armazenar seu valor. Também assuma que contador tem o valor 100.
int *m; // m e um ponteiro para o tipo intm = &contador; // m recebe o endereço de contadorq = *m // q recebe o valor que está no
// endereço de m
1a. instrucao: A variável m não é do tipo, mas aponta para um valor do tipo int.Após a 2a. instrução m terá o valor 2000. Após a 3a. instrução q terá o valor 100.
Quais as Saídas Produzidas I?
int destino=0, fonte=0, *m;fonte = 10;m = &fonte;destino = *m;printf(“%d”, destino);
Quais as Saídas Produzidas I?
int destino=0, fonte=0, *m;fonte = 10;m = &fonte;destino = *m;printf(“%d”, destino);
fonte destino
m
0 0 NULL
Quais as Saídas Produzidas I?
int destino=0, fonte=0, *m;fonte = 10;m = &fonte;destino = *m;printf(“%d”, destino);
m
NULL
destino0
fonte
10
Quais as Saídas Produzidas I?
int destino=0, fonte, *m;fonte = 10;m = &fonte;destino = *m;printf(“%d”, destino);
mdestino0
fonte
10
Quais as Saídas Produzidas I?
int destino=0, fonte, *m;fonte = 10;m = &fonte;destino = *m;printf(“%d”, destino);
mdestino10
fonte
10
Quais as Saídas Produzidas I?
int destino=0, fonte, *m;fonte = 10;m = &fonte;destino = *m;printf(“%d”, destino);
mdestino10
fonte
10
Quais as Saídas Produzidas II?
int x, *p1, *p2;x = 10;p1 = &x;p2 = p1;printf("%d", *p2)
Quais as Saídas Produzidas II?
int x=10, *p1, *p2;p1 = &x;p2 = p1;printf("%d", *p2)
xp2p1
10NULL NULL
Quais as Saídas Produzidas II?
int x=10, *p1, *p2;p1 = &x;p2 = p1;printf("%d", *p2)
xp2p1
10NULL
Quais as Saídas Produzidas II?
int x=10, *p1, *p2;p1 = &x;p2 = p1;printf("%d", *p2)
xp2p1
10
Quais as Saídas Produzidas II?
int x=10, *p1, *p2;p1 = &x;p2 = p1;printf("%d", *p2)
xp2p1
10
Quais as Saídas Produzidas III?
int a=1, b=2, *pa, *pb;printf("a=%d b=%d\n",a,b);pa=&a;pb=&b;*pa+=5;*pb+=5;printf("a=%d b=%d\n",a,b);
apbpa
1NULL NULL
b
2
Quais as Saídas Produzidas III?
int a=1, b=2, *pa, *pb;printf("a=%d b=%d\n",a,b);pa=&a;pb=&b;*pa+=5;*pb+=5;printf("a=%d b=%d\n",a,b);
apbpa
1NULL NULL
b
2
Quais as Saídas Produzidas III?
int a=1, b=2, *pa, *pb;printf("a=%d b=%d\n",a,b);pa=&a;pb=&b;*pa+=5;*pb+=5;printf("a=%d b=%d\n",a,b);
apbpa
1
b
2
Quais as Saídas Produzidas III?
int a=1, b=2, *pa, *pb;printf("a=%d b=%d\n",a,b);pa=&a;pb=&b;*pa+=5;*pb+=5;printf("a=%d b=%d\n",a,b);
apbpa
6
b
7
Quais as Saídas Produzidas III?
int a=1, b=2, *pa, *pb;printf("a=%d b=%d\n",a,b);pa=&a;pb=&b;*pa+=5;*pb+=5;printf("a=%d b=%d\n",a,b);
apbpa
6
b
7
Cuidado ao Manipular Ponteiros
Onde está o erro no código abaixo?
int x, *p;x = 10;*p = x;
Cuidado ao Manipular Ponteiros
Onde está o erro no código abaixo?
int x, *p;x = 10;*p = x;
O problema do código acima é que estamos atribuindo o conteúdo da variável x para alguma posição da memória desconhecida. Desconhecida porque p não tem parte de valor, só parte de posição (que é null), ou seja, p foi declarada mas nunca apontou para um endereço.
O certo seria:
int x, *p;x = 10;p = &x // p = (int) malloc(sizeof(int));
// *p = x;
Aritmética de Ponteiros
Existem apenas duas operações aritméticas que podem ser utilizadas com ponteiros: adição e subtração.
Cada vez que um ponteiro é incrementado, ele aponta para a posição de memória do próximo elemento de seu tipo base. O mesmo raciocínio quando decrementado.
Vamos analisar as instruções I, II e III:I. Vamos imaginar que p passe a apontar para o endereço 2000.II. Partindo do princípio que cada elemento do tipo int ocupa 2 bytes, o próximo elemento do tipo int está na posição 2002, então p passa a conter 2002 e não 2001. III. Estando p apontando para a posição 2002, ao incremetar 10, p passa a conter 2022.
a = 100;int *p; p=&a;
(I)
p++;
(II)
p=p+10;
(III)
Aritmética de Ponteiros
(I) a = 100;int *p;
p=&a;
(II) p++;
(III) p=p+10;
Comparação de Ponteiros
Qual a saída produzida?
main(){ int a,b,*p,*q; a=10; b=10; p=&a; q=&b; if (p==q) { printf("Valores iguais.\n"); }else{ printf("valores diferentes.\n"); } getch();}
Comparação de Ponteiros
Qual a saída produzida?
main(){ int a,b,*p,*q; a=10; b=10; p=&a; q=&b; if (p==q) { printf("Valores iguais.\n"); }else{ printf("valores diferentes.\n"); } getch();} Porque?
Ponteiros e Vetores
Em C, o nome de um vetor sem índice retorna o endereço inicial do vetor, que é o primeiro elemento. No trecho de código abaixo, p recebe o endereço do 1o. elemento da vetor frase.
char frase[80], *p;p = frase;
Diferentes Formas de Acessar Um Vetor
C fornece dois métodos para acessar os elementos de um vetor: Indexação e Aritmética de ponteiros.
Para acessarmos o 5o. elemento da vetor frase do exemplo anterior usamos uma das formas abaixo:
char frase[80], *p;p = frase;
frase[4] (1)*(p+4) (2)*(frase + 4) (3)p[4] (4)
As linhas 1 e 4 utilizam indexação para acessar o vetor e as linhas 2 e 3 utilizam aritmética de ponteiros.
frase
0
1
2
3
4
...
79
Diferentes Formas de Acessar Um Vetor
main(){int vet[]={10, 20, 30,40,50};
/*Se o nome do vetor sozinho é um ponteiro
para o 1o. elemento, então *(vet) fazreferência ao conteúdo deste endereço.
*/
for (int i=0; i < 5; i++){ printf("%d\n",*(vet+i)); } getch();}
Diferentes Formas de Acessar Um Vetor
int *p=vet; for (int i=0; i < 5; i++){ printf("%d\n",*(p+i)); // ou printf("%d\n",*(vet+i));} /*forma tradicional de se referenciar a um vetor.*/
for (int i=0; i < 5; i++){ printf("%d\n",vet[i]);}
/*Em C qualquer ponteiro pode ser indexado como se fosse um vetor.*/
for (int i=0; i < 5; i++){ printf("%d\n",p[i]);}
1
3
4
2
Vetor de Ponteiros
Ponteiros podem ser organizados em vetores como qualquer outro tipo de dado.
A declaração de um vetor de 10 ponteiros inteiros: int *x[10];
Para atribuir o endereço de uma variável int, chamada num, ao 5o. elemento do vetor de ponteiros: x[4]=#
Para acessar a mesma posição seria: *x[4]
Passando Argumentos por Referência
O endereço de um argumento é copiado no parâmetro formal da função. Dentro da função, o endereço é usado para acessar o argumento real utilizado na chamada da função.
Isto significa que alterações feitas nos parâmetros formais afetam os argumentos usados na chamada da função.
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
a
10
b
20
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
ayx
10
b
20
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
ayx
10
b
20
temp
10
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
ayx
20
b
20
temp
10
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
ayx
20
b
10
temp
10
Passando Argumentos por Referência
void troca(int *x, int *y);
main(){ int a=10; int b=20; troca(&a,&b); printf("%d %d", a,b); getch();}void troca(int *x,int *y){ int temp; temp=*x; *x=*y; *y=temp;}
a
20
b
10
Vetores como Argumento para uma Função
Em C, quando um vetor é usado como um argumento para uma função, apenas o endereço do primeiro elemento do vetor é passado, não uma cópia do vetor inteiro.
int i[10];func(i);
Podemos declarar o parâmetro formal de três formas:
1. void func(int *x) como um ponteiro
2. void func(int x[10]) como uma matriz dimensionada
3. void func(int x[]) como uma matriz não dimensionada
Podemos verificar que o comprimento da matriz não importa para a função, porque C não realiza verificação de limites. Portanto void func(int x[30]) também funcionaria, porque o compilador C instrui func a receber um ponteiro, ele não cria realmente um vetor de 10 ou de 30 elementos.
Vetores como Argumento para uma Função
#define Max 5
int numeros[MAX]; void mostra(int *p, int n){ for (int i=0; i<n; i++){ printf("\n%d",*(p++)); // ou printf("\n%d",p[i]) }} main(void){ for(int i=0;i<MAX;i++){ numeros[i]=i*10; } mostra(numeros, MAX); getch();}
Vetores como Argumento para uma Função
Quando um vetor bidimensional é usada como argumento para uma função, apenas um ponteiro para o primeiro elemento é realmente passado. Porém, uma função que recebe um vetor bidimensional como parâmetro, deve definir pelo menos o comprimento da 2a. dimensão. Isto ocorre porque o compilador C precisa saber o comprimento de cada linha para indexar a vetor corretamente.
Exemplo: Uma função que recebe um vetor bidimensional de inteiros 10 x 10 deve ser declarada da seguinte forma: void func (int x[ ] [10]);
É importante entender que, quando um vetor é usado como um argumento para uma função, seu endereço é passado para a função. Portanto existe a possibilidade do conteúdo original da vetor ser alterado.
Função Retornando um Ponteiro
Indicamos que uma função retornará um ponteiro inserindo um * antes do nome da função. int *func();