1
Ponteiros
Ponteiros
matrizes, fun
matrizes, funç
ções
ões
e novos tipos
e novos tipos
9. Ponteiros: matrizes, fun
9. Ponteiros: matrizes, funç
ções e novos tipos
ões e novos tipos
2
O
armazenamento
de
informações ocupa espaço.
No caso do computador, é útil
representar
o
espaço
disponível por meio de uma
grade. Cada bloco da grade
pode armazenar 1 byte. Com
1 byte é possível armazenar
uma
informação
do
tipo
caractere (char). Variáveis de
outros tipos ocupam mais
espaço (int – 4 bytes, float – 4
bytes e float – 8 bytes).
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Definição - Armazenamento
Exemplo 1
Uma variável tipo char é armazenada na memória.
Espaço de Memória char ch = ‘a’;
3
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Para manipular mais de uma informação é necessário um sistema que organize as informações a partir de uma identificação unívoca. Na linguagem C, cada vez que é declarada uma variável (por exemplo, char ch1) é associado um número hexadecimal (por exemplo, 0022FF77) que é denominado de endereço (para manter a clareza, mas sem perda de generalidade, serão utilizados números na base 10 em todos os exemplos descritos a seguir). Esse número realiza a associação entre o nome de uma variável em um programa em C com o espaço físico que ela ocupa na memória do computador. Sem o uso de endereços não seria possível distinguir ou recuperar informações armazenadas na memória do computador.
Organizando as informações com variáveis
4
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Exemplo 2
ch1 = ‘a’; ‘a’ 5000 ch1 5001 5002 5003 5004 char ch1; 5000 ch1 5001 5002 5003 5004 O exemplo a seguir mostra a associaçãoentre nome e endereço de uma variável.
Reserva espaço de memória
Armazena ‘a’ no endereço
Associa ch1 a um endereço
5
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
char ch2; ‘a’ ch1 ch2 ch2 = ‘b’; ‘a’ ch1 ‘b’ ch2 printf(“%c”,ch2); ch2 0022FF7A ‘b’
Busca associação do nome ch2 com endereço de memória.
Busca conteúdo na memória 5000 5001 5002 5003 5004 5000 5001 5002 5003 5004 6
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Na linguagem C é possível saber o endereço de uma
variável através do operador &. O endereço de uma
variável pode ser impresso ou lido com printf ou scanf
a partir das tags %x ou %X.
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Definição – Operador de Endereço (&)
Exemplo 3
Obtendo informação de endereço da variável ch através do operador de endereço &. main() { char ch = ‘a’; printf(“Conteudo de ch = %c”, ch); printf(“Endereco de ch = %x”, &ch); } a 5000 ch 5001 5002 Em termos de memória7
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Na linguagem C existe um tipo de variável capaz de
armazenar o endereço de uma variável, denominado de
tipo ponteiro. No entanto, deve-se sempre especificar o
tipo da variável cujo endereço o ponteiro irá armazenar.
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Definição – Ponteiros (tipo *ponteiro)
A declaração de uma variável do tipo ponteiro (ou seja, capaz de armazenar o endereço) de uma variável do tipo caractere é:
char * p; ou char* p; ou char *p;
A partir da declaração acima a variável p é capaz de armazenar o endereço de qualquer variável do tipo caractere. Em geral,
a terceira forma de declaração de ponteiros é a mais usada. A leitura ou impressão do conteúdo de uma variável do tipo ponteiro com scanf ou printf, respectivamente, usa a tag %p.
8
Exemplo 4
Armazenando informação de endereço da variávelch em uma variável do tipo ponteiro. main()
{
char *p, ch = ‘a’;
// Armazenando endereço de ch1 // na variável p que é ponteiro para // caractere. p = &ch; printf(“Conteudo de ch = %c”, ch); printf(“Endereco de ch = %x”, &ch); printf(“Conteudo de p = %p”, p); printf(“Endereco de p = %x”, &p); } a 5000 ch 5001 5001 5002 p Em termos de memória
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
9.1 Ponteiros: Endere
ço
o
9.1 Ponteiros: Endereç
ço
o
9
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Na linguagem C, o operador * além de servir para declarar
que um variável é do tipo ponteiro serve também para
saber qual o valor contido no endereço armazenado
(apontado) por uma variável do tipo ponteiro.
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Definição – Operador de dereferência (*)
#include <stdio.h> main() { char *p, ch = ‘a’; p = &ch1; printf(“Conteudo de ch = %c”, ch); printf(“Endereco de ch = %x”, &ch); printf(“Conteudo de p = %p”, p); printf(“Conteudo de ch = %x”, *p);}
Exemplo 5
a 5000 ch 5000 7000 p Usando operador **p
109.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Uma vez que as variáveis do tipo ponteiro ocupam um
endereço de memória é possível criar uma nova variável
para também armazenar este endereço. Ou seja, é
possível criar ponteiros para ponteiros.
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
ço
o
Definição – Ponteiros para Ponteiros (tipo **ponteiro)
10 5000 x 5001 5000 5002 p_x 5003 5002 5004 p_p_x int x, *p_x, **p_p_x; x = 10; p_x = &x; p_p_x = &p_p_x;
11
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
9.1 Ponteiros: Endere
ço
o
9.1 Ponteiros: Endereç
ço
o
10 5000 x 5000 5002 p_x 5002 5004 p_p_x O esquema a seguir ilustra a utilização de ponteiros, o
operador de endereço & (obtém o endereço de memória de uma dada variável) e o operador de dereferência * (obtém o conteúdo de um endereço de memória). O resultado da expressão **p_p_x é mais facilmente obtido se obtido em etapas: *(*(p_p_x))→→→→*(*(5002)) →→→→*(5000) →→→→10. *p_p_x
*
=
p_x **p_p_x=
*p_x*
=
10 & &=
5000 &x=
5000 &p_x=
5002 12Exemplo 6
Utilizando ponteiros para ponteiros e osoperadores de endereço (&) e dereferência (*). #include <stdio.h> main() { int x, *p_x, **p_p_x; p_x = &x; p_p_x = &p_x; printf(“Conteudo x = %d”, x); printf(“Endereco x = %x”, p_x); printf(“Endereco p = %p”, p_p_x); printf(“Conteudo x = %d”, *p_x); printf(“Conteudo x = %d”, **p_p_x); printf(“Endereco p_x = %x”, &p_x); } Em termos de memória
9.1 Ponteiros: Endere
9.1 Ponteiros: Endereç
9.1 Ponteiros: Endere
ço
o
9.1 Ponteiros: Endereç
ço
o
10 5000 x 5000 5002 p_x 5002 5004 p_p_x *p_p_x *p_x &p_x &x
13
Ponteiros
Ponteiros
-
-
Parte2
Parte2
matrizes, fun
matrizes, funç
ções
ões
e novos tipos
e novos tipos
9. Ponteiros: matrizes, fun
9. Ponteiros: matrizes, funç
ções e novos tipos
ões e novos tipos
14
9.1 Ponteiros: Aritm
9.1 Ponteiros: Aritmé
é
tica
tica
Uma variável do tipo ponteiro está sempre associada a um
tipo. Ou seja, um ponteiro para um dado tipo t endereça
o número de bytes que esse tipo t ocupa em memória,
isto é, endereça sizeof(t) bytes. Se um ponteiro para
uma variável do tipo t, que armazena o endereço x, for
incrementada através do operador ++, automaticamente
este ponteiro passará a ter o valor x + sizeof(t).
Definição – Aritmética de Ponteiros
char ch, *p; ch = ‘a’; p = &ch; p++; 5000 7000 p 5001 7000 p++ Tipo char só ocupa 1 byte !
15
9.1 Ponteiros: Aritm
9.1 Ponteiros: Aritmé
é
tica
tica
int x, *p; x = 10; p = &x; p++; 5000 7000 p 5004 7000 p++ Tipo int ocupa 4 bytes ! x 5000 p x 5001 x 5002 x 5003 ? 5004 p++ ? 5005 ? 5006 ? 5007 Espaço de 4 bytes paraarmazenar a variável x tipo int.
A operação p++ percorre sizeof(tipo p) bytes ! Memória Em Detalhes 16
9.1 Ponteiros: Aritm
9.1 Ponteiros: Aritmé
é
tica
tica
A aritmética de ponteiros é particularmente importante para manipulação de vetores e strings. Isto porque quando declaramos um vetor seus elementos são alocados em espaços de memória vizinhos. Além disso, o nome de um vetor equivale ao endereço do primeiro elemento dele, ou seja, se um vetor possui nome v, então, v equivale à &v[0] ( v
↔ ↔↔
↔&v[0]).
Relação entre ponteiros e vetores
‘a’ ‘b’ 5001 v[1] ‘c’ 5002 v[2] main() { char *ptr, v[3]={‘a’, ‘b’, ‘c’}; ptr =v; printf(“v[0] = %d”, *(ptr)); printf(“v[1] = %d”, *(++ptr)); printf(“v[2] = %d”, *(++ptr)); } v[0] 5000
*
ptr+1 ptr+2 ptr17
9.3 Ponteiros: Vetores
9.3 Ponteiros: Vetores
1 0 v[0] 5000 2 0 v[1] 5004 3 0 v[2] 5008 Em termos de memória #include <stdio.h> main() { int i, *p, v[3]; p = &v[0]; // ou p = &v;// Usando indices para acessar v.
printf(“Usando v[i]:”);
for (i=0; i < 3; i++)
{
scanf(“%d”, &v[i]); printf(“ [%4d] ”, v[i]); }
// Usando ponteiros.
printf(“Usando v[i]:”);
for (i=0; i < 3; i++, ptr++)
{ scanf(“%d”, ptr); printf(“ [%4d] ”, *ptr); } } Exemplo 7 – Vetor+Ponteiro
Observe que &v[i] equivale à ptr !
1 0 v[0]
5000
Observe, ainda, que:
&
*
18
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ção Dinâmica de Mem
ão Dinâmica de Memó
ória
ria
Alocação Estática de Memória p/ v
#include <stdio.h>
main()
{
int i, s, v[12];
// Só usa 3 blocos de 4 bytes !
s = 0;
for (i=0; i < 3; i++)
{
printf(“ Entre com v[%d]: ”, i); scanf(“%d”, &v[i]);
// Contando bytes usados.
s = s + sizeof(v[i]); }
for (i=0; i < 3; i++)
printf(“\n [%4d] \n”, v[i]);
// Mostra o uso da memória.
printf(“Bytes usados = %d \n”,s); s = sizeof(v);
printf(“Bytes de v = %d \n”,s); }
Exemplo 8.1 – Alocação 1
Quando o vetor v é declarado são reservados 12 espaços para elementos do tipo int. Como cada
elemento de v é do tipo int e ocupa 4 bytes, então, é realizada a alocação de 12 * 4 =
48 bytes para o vetor v ! Observe, porém, que só são usados os elementos v[0], v[1]
e v[2], ou seja, 3*4 = 12 bytes de um total de 48 bytes.
19
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ção Dinâmica de Mem
ão Dinâmica de Memó
ória
ria
Alocação Dinâmica de Memória#include <stdio.h>
main()
{int i, n; float *nota = NULL; printf(“Entre com tamanho n: ”); scanf(“%d”,&n);
// Aloca 4*n bytes de memória em // tempo de execução com
// ponteiro!
nota=(float *)calloc(n,sizeof(float));
for (i=0; i < n; i++)
{
printf(“ Entre a nota %d:”, i+1); scanf(“%f”, nota+i);
}
for (i=0; i < n; i++)
printf(“\n [%4f] \n”, *(nota+i));
// Mostra o uso da memória.
s = n*sizeof(float);
printf(“Bytes de nota = %d \n”,s); } Exemplo 8.2 – Alocação 2 A alocação dinâmica de memória consiste em determinar em tempo de execução o quanto de memória será utilizado. A
função calloc(n, size_t) fornece o primeiro endereço de memória de um bloco de n espaços com size_t bytes. No exemplo acima são alocados
3*sizeof(float)=3*4 = 12 bytes. 0022FF77 calloc(3, 4);
20
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ção Dinâmica de Mem
ão Dinâmica de Memó
ória
ria
Alocação Dinâmica de Memória#include <stdio.h>
main()
{int i, n; float *nota = NULL; printf(“Entre com tamanho n: ”); scanf(“%d”,&n);
// Aloca 4*n bytes de memória em // tempo de execução com
// ponteiro!
nota=(float *)malloc(n*sizeof(float));
for (i=0; i < n; i++)
{
printf(“ Entre a nota %d:”, i+1); scanf(“%f”, nota+i);
}
for (i=0; i < n; i++)
printf(“\n [%4f] \n”, *(nota+i));
// Mostra o uso da memória.
s = n*sizeof(float);
printf(“Bytes de nota = %d \n”,s); }
Exemplo 8.3 – Alocação 3
A função malloc(n*size_t) fornece o primeiro endereço de memória de um bloco de n espaços com size_t bytes. No exemplo acima são alocados
3*sizeof(float) = 3*4 = 12 bytes. A função malloc possui a mesma utilidade da
função calloc, apenas sua sintaxe é diferente. 0022FF77 malloc(3*4);
21
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ção Dinâmica de Mem
ão Dinâmica de Memó
ória
ria
Alocação Dinâmica de Memória#include <stdio.h>
main()
{int i, n; float *nota = NULL; printf(“Entre com tamanho n: ”); scanf(“%d”,&n);
// Aloca 4*n bytes de memória em // tempo de execução com
// ponteiro!
nota=(float *)malloc(n*sizeof(float));
for (i=0; i < n; i++)
{
printf(“ Entre a nota %d:”, i+1); scanf(“%f”, nota+i);
}
for (i=0; i < n; i++)
printf(“\n [%4f] \n”, *(nota+i));
// Libera memória alocada cujo // endereço inicial está em nota.
free(nota);
}
Exemplo 8.4 – Alocação 4
Quando é realizada alocação estática de memória o programa em C se encarrega de liberar a memória utilizada.
Mas, quando a alocação dinâmica de memória é empregada, através de malloc
ou calloc, é necessário, antes de terminar o uso do programa, liberar a memória
através do comando free(). 0022FF77 free();
22
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ção Dinâmica de Mem
ão Dinâmica de Memó
ória
ria
Alocação Estática de Memória Alocação Dinâmica de Memória Comando int v[3]; Liberação Memória O próprio programa, ao ser encerrado, se encarrega de liberar a memória alocada.
Se não for usar mais a variável v, então, é necessário empregar o comando free(): Reserva 3 espaços de 4 bytes em v: v = (int *)malloc(n,4); Reserva n espaços de 4 bytes em v: •••• •••• •••• free(v);
23
Ponteiros
Ponteiros
-
-
Parte3
Parte3
matrizes, fun
matrizes, funç
ções
ões
e novos tipos
e novos tipos
9.4 Ponteiros: matrizes, fun
9.4 Ponteiros: matrizes, funç
ções e novos tipos
ões e novos tipos
24 Assim como é possível alocar memória em tempo de execução
para armazenar um vetor v com n posições de memória com tamanho igual a sizeof(float) através comando calloc(n, sizeof(float)), também, é possível construir uma matriz M com m linhas e n colunas. Os comandos para tal tarefa são como dados a seguir:
Relação entre ponteiros, vetores e matrizes
main()
{ char **M; int m, n, i; printf(“Entre com m e n”); scanf(“%d %d”,&m, &n);
// Vetor de endereços (os elementos são do tipo char *). M = (char **) calloc(m,sizeof(char *));
// Cria para cada endereço um vetor de elementos char. for(i = 0; i < m; i++) *(M + i) = calloc(n, sizeof(char)); }
9.4 Ponteiros: Aloca
25 Para melhor entender os comandos anteriores é necessário enxergar a forma como os elementos de M serão armazenados
ao se empregar ponteiros, supondo m = 3 e n = 4:
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ç
ão Dinâmica de Memó
ão Dinâmica de Mem
ória
ria
M[0] M[1] M[2] 5000 5001 5002 6000 7000 8000
// Vetor de endereços (os elementos são do tipo char *). M = (char **) calloc(3,sizeof(char *));
M recebe o endereço 5000 que é o endereço do primeiro elemento
de um vetor cujos elementos são endereços iniciais de vetores
cujos elementos são tipo char.
Conteúdo de M[i] ou *(M+i) Endereço de M[i] ou (M+i) 26
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ç
ão Dinâmica de Memó
ão Dinâmica de Mem
ória
ria
M[0] M[1] M[2] 5000 5001 5002 6000 7000 8000 M[0][0] 6000 ‘a’ M[0][1] 6001 ‘b’ M[0][2] 6002 ‘c’ M[0][3] 6003 ‘d’ M[1][0] 7000 ‘e’ M[1][1] 7001 ‘f’ M[1][2] 7002 ‘g’ M[1][3] 7003 ‘h’ M[2][0] 8000 ‘i’ M[2][1] 8001 ‘j’ M[2][2] 8002 ‘k’ M[2][3] 8003 ‘l’ // Cria para cada endereço um vetor de elementos char. for(i = 0; i < 3; i++) *(M + i) = calloc(4, sizeof(char));
Criação de vetor do tipo char para *(M+0).
27 Para melhor entender os comandos anteriores é necessário enxergar a forma como os elementos de M serão armazenados
ao se empregar ponteiros, supondo m = 3 e n = 4:
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ç
ão Dinâmica de Memó
ão Dinâmica de Mem
ória
ria
M[0] M[1] M[2] 5000 5001 5002 6000 7000 8000 M[0][0] 6000 ‘a’ M[0][1] 6001 ‘b’ M[0][2] 6002 ‘c’ M[0][3] 6003 ‘d’ Conteúdo de M[i][j] ou *(*(M+i)+j) Exemplo: M = 5000, i = 0 e j = 2 *(*(M+i)+j) = *(*(5000+0)+2) = *(6000+2) = ‘d’ M[0] 6000 5000 M[0][2] 6002 ‘c’
*
*
289.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ç
ão Dinâmica de Memó
ão Dinâmica de Mem
ória
ria
#include <stdio.h> #include <stdlib.h>
main()
{int i, j, m, n;
float **M = NULL;
printf(“Entre com m e n: ”); scanf(“%d %d”, &m, &n);
// Aloca m espaços tipo float *.
M=(float **)calloc(m, sizeof(float *));
// Aloca n espaços tipo float, cada M[i].
for (i=0; i < m; i++)
M[i] = (float *)calloc(n, sizeof(float));
// Preenchendo a matriz M usando // índices: M[i][j].
for (i=0; i < m; i++)
{ printf(“Aluno %d: ”,i+1);
for (j=0; j < n; j++)
{ printf(“ Nota %d:”, j+1); scanf(“%f”, &M[i][j]); } }
Exemplo 9 – Notas + Matrizes Exemplo 9 – Notas + Matrizes
// Impressão dos elementos de M, // empregando ponteiros.
for (i=0; i < m; i++)
{ for (j=0; j < n; j++)
printf(“ [%4f] ”, *(*(M+i)+j)); printf(“ \n ”);
}
// Liberação de memória.
// Liberando m vetores de tamanho n.
for (i=0; i < m; i++) free(M[i]);
// Liberando o vetor de ponteiros // de tamanho m.
free(M);
29
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ç
ão Dinâmica de Memó
ão Dinâmica de Mem
ória
ria
M[0] M[1] M[2] 5000 5001 5002 6000 7000 8000 M[0][0] 6000 ‘a’ M[0][1] 6001 ‘b’ M[0][2] 6002 ‘c’ M[0][3] 6003 ‘d’ M[1][0] 7000 ‘e’ M[1][1] 7001 ‘f’ M[1][2] 7002 ‘g’ M[1][3] 7003 ‘h’ M[2][0] 8000 ‘i’ M[2][1] 8001 ‘j’ M[2][2] 8002 ‘k’ M[2][3] 8003 ‘l’ Cada comando free libera um vetor de cada vez:
for (i=0; i < m; i++) free(M[i]);
free(M);
30
9.4 Ponteiros: Aloca
9.4 Ponteiros: Alocaç
ç
ão Dinâmica de Memó
ão Dinâmica de Mem
ória
ria
M[0][0] 6000 ‘a’ M[0][1] 6001 ‘b’ M[0][2] 6002 ‘c’ M[0][3] 6003 ‘d’ M[1][0] 7000 ‘e’ M[1][1] 7001 ‘f’ M[1][2] 7002 ‘g’ M[1][3] 7003 ‘h’ M[2][0] 8000 ‘i’ M[2][1] 8001 ‘j’ M[2][2] 8002 ‘k’ M[2][3] 8003 ‘l’
Cada comando free libera um vetor de cada vez:
for (i=0; i < m; i++) free(M[i]);
free(M[0]);
free(M[1]);
31
9.6 Ponteiros: Passagem de parâmetros
9.6 Ponteiros: Passagem de parâmetros
Toda função na linguagem C possui 4 partes:
char misterio ( int n, char c ) { char c2; c2 = (char) (c+n); return c2; } (2) Nome: misterio (1)Tipo de Retorno:char (3)Lista Parâmetros: (int n, char c)
(4) Corpo da Função: Comandos entre { }
32
9.6 Ponteiros: Passagem de parâmetros
9.6 Ponteiros: Passagem de parâmetros
char misterio( int n, char c ) { char c2; c2 = (char) (c+n); return c2; } ch =misterio( 5, ‘a’); ch =(char)(5+‘a’);
Ao esquema ao lado ilustra como é realizada a passagem de parâmetros:
Passo 1: Uma função chama a função misterio e fornece os Valores iniciais para n e c. Passo 2: É retornado o valor da variável c2 obtido após a avaliação de uma expressão. Passo 3: O fluxo de execução do programa é retornado ao ponto de chamada. A palavra misterio(5, ‘a’) é substituída pelo valor retornado.
Chamada à misterio Retorna valor de misterio Executa misterio Passo 1 Passo 2 Passo 3 Explicação
33
9.6 Ponteiros: Passagem de parâmetros
9.6 Ponteiros: Passagem de parâmetros
No exemplo anterior pode-se dizer que ocorreu uma
passagem de parâmetros por valor, pois uma cópia dos
valores das variáveis é passada através da chamada à função misterio. Esses valores são atribuídos as variáveis locais de misterio (ou seja, as variáveis n e c cujo escopo
é apenas a função misterio) e esses valores são manipulados e o resultado dessa manipulação é armazenado em uma variável local c2. Para retornar o
resultado dessa manipulação é empregado a palavra reservada return, bem como é especificado um tipo de
retorno no parâmetro de saída coerente (char). Para ilustrar em termos de memória como é feita a passagem
de parâmetros por valor, segue o Exemplo 10.
34
9.6 Ponteiros: Passagem por valor
9.6 Ponteiros: Passagem por valor
#include <stdlib.h> #include <stdio.h>
int Maior(int x, int y) { return (x > y) ? x:y; } main() { int a, b, max;
printf(“Entre com a e b:”); scanf(“%d %d”, &a, &b); max = Maior(a,b);
printf(“Max{a,b} = %d \n”,max); }
5000
Exemplo 10: Programa Maior Escopo da Função Main
a
5050 5 b
6000
Escopo da Função Maior
x 7050 5 y 4 4 return 5;
35
9.6 Ponteiros: Passagem por valor
9.6 Ponteiros: Passagem por valor
#include <stdlib.h> #include <stdio.h>
int Maior(int x, int y) { return (x > y) ? x:y; } main() { int a, b, max;
printf(“Entre com a e b:”); scanf(“%d %d”, &a, &b); max = Maior(a,b);
printf(“Max{a,b} = %d \n”,max); }
Exemplo 10: Programa Maior No programa maior a função main chama a função Maior e passa os valores contidos nas variáveis a e b (cujo escopo é a função main) para as variáveis x e y (cujo escopo é a função Maior). Diz-se que este tipo de passagem de parâmetros é
passagem por valor, pois é passada uma cópia dos valores das
variáveis contidas em um função para as variáveis de outra função. Observe que para isto, a função Maior tem especificado um parâmetro de saída (tipo int) e emprega o comando return.
36
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
Existe, ainda, uma outra forma de passagem de parâmetros denominada passagem por referência. Neste caso, não é
passado o valor de uma variável e sim o seu endereço. A partir deste endereço é possível manipular uma variável
x declarada em função f1 (ou seja, em princípio o escopo de manipulação da mesma é restrito a função f1) a partir
de uma outra função f2. Basta, para tanto, que uma variável local y de f2 seja declarada do tipo ponteiro (capaz de armazenar endereços de memória de um dado
tipo) e receba o endereço da variável x declarada em f1. Qualquer modificação realizada y em f2 irá, ao término de
f2, será acarretar em mudanças em x de f1, também. Um exemplo disso é ilustrado com o Exemplo 11.
37
#include <stdlib.h> #include <stdio.h>
int Troca( int *x, int *y ) { int tmp = *x; *x = *y; y = tmp; } main() {int a, b, max;
printf(“Entre com a e b:”); scanf(“%d %d”, &a, &b); printf(“a=%d - b=%d\n”,a,b); Troca(&a, &b);
printf(“a=%d - b=%d\n”,a,b);} Exemplo 11: Programa Troca
Escopo da Função main
a
5 b
6000
Escopo da Função Troca
x 7050 5050 y 4 5000
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
5000 5050
38 Função Troca
int Troca(int *x, int *y) { int tmp = *x; *x = *y; *y = tmp; } 8050 4 tmp 6000 x 5000 a 4 5000
*
Função main P1 P2O comando é realizado em dois passos: (P1) Como o endereço de a foi passado para x, usando *x obtém-se o valor contido em a. (P2) O valor contido em a é passado para tmp (repare que tmp é tipo int e x é int *).
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
39 Função Troca
int Troca(int *x, int *y) { int tmp = *x; *x = *y; *y = tmp; } 7050 y 5050 b 5 5050
*
Função main P1O comando é realizado em três passos: (P1) Com *y obtém-se o valor contido em b. (P2) Com *x obtém-se o valor contido em a. (P3) O valor de b (5) é passado para a.
6000 x 5000 a 4 5000 Função Main Função Troca P2
*
5 P39.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
40 Função Troca
int Troca(int *x, int *y) { int tmp = *x; *x = *y; *y = tmp; } 8050 4 tmp 7050 y 5050 b 5 5050
*
Função main P1 P2O comando é realizado em dois passos: (P1) Como o endereço de a foi passado para y, usando *y obtém-se o valor contido em b. (P2) O valor contido em tmp é passado para b (repare que tmp é tipo int e y é int *).
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
41
#include <stdlib.h> #include <stdio.h>
int Troca(int *x, int *y) { int tmp = *x; *x = *y; y = tmp; } main() {int a, b, max;
printf(“Entre com a e b:”); scanf(“%d %d”, &a, &b); printf(“a=%d - b=%d\n”,a,b); Troca(&a, &b);
printf(“a=%d - b=%d\n”,a,b);}
Exemplo 11: Programa Troca Escopo da Função main
a
5 b
Função Troca 4
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
5000 5050
Escopo da Função Troca
a 4 b 5 5000 5050 42
É importante observar que a discussão anterior entre passagem por valor X passagem
por referência aparentemente não é válida para vetores tal como descrito no Exemplo 13. Observe que a função preencheV
modifica os valores contidos em V sem empregar o comando
return e sem possuir um parâmetro de retorno (o tipo de
retorno de preencheV é void) ! A explicação para isto é que a chamada a função preencheV
passa o endereço de V[0].
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
#include <stdio.h>
void printV(int X[], int n)
{ int i;
for (i = 0; i < 10; i++)
printf(“ [%4d] ”,X[i]); puts(“”);}
void preencheV(int X[], int n)
{ int i; for (i = 0; i < 10; i++) X[i] = 2*i + 1;} main ( ) { int i, n=10, V[10] = {0}; printV(V, n); preencheV(V, n); printV(V, n); }
43
#include <stdio.h>
void printV(int X[], int n)
{ int i;
for (i = 0; i < 10; i++)
printf(“ [%4d] ”,X[i]); puts(“”);}
void preencheV(int X[], int n)
{ int i; for (i = 0; i < 10; i++) X[i] = 2*i + 1;} main ( ) { int i, n=10, V[10] = {0}; printV(V, n); preencheV(V, n); printV(V, n); }
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
Escopo da Função main V[0]
Função preencheV 0
5000
Escopo da Função preencheV Exemplo 12–Referência ou Valor
V[1] 0 5004 ••• ••• ••• ••• ••• ••• ••• ••• ••• ••• ••• ••• V[9] 0 5036 X[0] 1 5000 X[1] 3 5004 ••• ••• ••• ••• ••• ••• ••• ••• ••• ••• ••• ••• X[9] 17 5036 44
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
No exemplo anterior, a função main contém uma chamada a função preencheV através do comando: preenche(V, n). É importante observar que esta chamada é tal que fornece
o endereço inicial do primeiro elemento de V (ou seja, &V[0], pois lembre-se que V ↔↔↔↔&V[0]) e o valor contido em
n. Assim, o endereço do primeiro elemento de V é associado ao nome do vetor X e qualquer modificação realizada nos elementos de X na verdade estará alterando
o conteúdo dos elementos do vetor V. Ou seja, a notação V[i] é um atalho para um conjunto de endereços de memória e T também é vinculado a este mesmo conjunto de endereços ao se realizar a chamada a função preencheV.
Ou seja, quando são empregados vetores na chamada de
45
9.6 Ponteiros: Passagem por referência
9.6 Ponteiros: Passagem por referência
#include <stdio.h>
void printV(int *X, int n)
{ int i;
for (i = 0; i < 10; i++)
printf(“ [%4d] ”,*(X+i)); puts(“”);}
void preencheV(int *X, int n)
{ int i; for (i = 0; i < 10; i++) *(X+i) = 2*i + 1;} main ( ) { int i, n=10, V[10] = {0}; printV(V, n); preencheV(V, n); printV(V, n); } Exemplo 13 – Ponteiros
Dado que a passagem de vetores é realizada por referência (ou seja, endereço de memória), é possível empregar a notação de
ponteiros na definição das funções, bem como no acesso aos
elementos de V através do nome X. Para tanto, pode-se usar a
notação que emprega a aritmética de ponteiros mais o operador de dereferência para acessar o elemento V[i], ou
seja, empregar *(X+i) tal como ilustrado no Exemplo 14.
Observação
46
9.7 Ponteiros: Passagem por Referência
9.7 Ponteiros: Passagem por Referência
#include <stdlib.h> #include <stdio.h> #include <conio.h>
// Definindo o novo tipo PESSOA.
typedef struct PESSOA
{ char nome[100];
float salario; char sexo;} PE;
// Protótipos das funções.
void Leitura(PE *V, int n); void Imprime(PE *V, int n);
main ( ) // Função Principal
{ PE *V; int i, n; printf(“Tamanho de V:”); scanf(“%d”, &n);
V = (PE *) calloc(n, sizeof(PE));
Exemplo 14–Cadastro+Funções Exemplo 14–Cadastro+Funções Leitura(V, n);
Imprime(V, n);
free(V);} // Fim da função main
void Leitura(PE *V, int n)
{int i;
for (i=0; i < n; i++)
{ printf(“Cadastro %d \n”, i+1); fflush(stdin);
printf(“Entre com o nome: ”); gets((V+i)->nome);
printf(“Entre com salario: ”); scanf(“%f”,&((V+i)->salario)); printf(“Entre com sexo: ”); (V+i)->sexo = getche(); fflush(stdin);
47
void Imprime(PE *V, int n)
{ int i;
for (i=0; i < n; i++)
{ printf(“--- \n”); printf(“Cadastro %4d: \n”, i+1); printf(“Nome: %s \n”,
(*(V+i)).nome);
printf(“Salario: %f \n”, V[i].salario); printf(“Sexo: %c \n”, (V+i)->sexo); printf(“---\n\n”); }
printf(“\n”); }
Exemplo 14–Cadastro+Funções
9.7 Ponteiros: Passagem por Referência
9.7 Ponteiros: Passagem por Referência
No Exemplo 15 é empregada a alocação dinâmica de memória em conjunto com um novo tipo (o tipo PESSOA) de modo a se Poder criar um vetor V cujos elementos são do tipo PESSOA. Assim, cada elemento V[i] possui 3 campos: nome, salario e sexo. Para acessar a informação destes campos existem 3 formas equivalentes:
(i) V[i].salario (ii) (*(V+i)).salario
(iii) (V+i)->salario