• Nenhum resultado encontrado

C Aula10PonteirosPartes1e2e3

N/A
N/A
Protected

Academic year: 2021

Share "C Aula10PonteirosPartes1e2e3"

Copied!
24
0
0

Texto

(1)

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’;

(2)

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ção

entre 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

(3)

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ória

(4)

7

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ável

ch 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

(5)

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

10

9.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;

(6)

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 12

Exemplo 6

Utilizando ponteiros para ponteiros e os

operadores 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

(7)

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 !

(8)

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 para

armazenar 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 ptr

(9)

17

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.

(10)

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);

(11)

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);

(12)

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

(13)

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).

(14)

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’

*

*

28

9.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);

(15)

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]);

(16)

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

(17)

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;

(18)

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.

(19)

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 P2

O 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

(20)

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 P1

O 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 P3

9.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 P2

O 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

(21)

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); }

(22)

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

(23)

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);

(24)

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

Referências

Documentos relacionados

XI – ter autonomia para definir produção, programação e distribuição de conteúdo no sistema público de radiodifusão, em consonância com o seu Conselho

A baixa taxa de desconto ao longo dos anos de produção do campo, para o cálculo da função objetivo, aliada a baixa produção de água que a locação de

Local de realização da avaliação: Centro de Aperfeiçoamento dos Profissionais da Educação - EAPE , endereço : SGAS 907 - Brasília/DF. Estamos à disposição

Declaro ser esclarecido e estar de acordo com os seguintes pontos: O trabalho ESTUDO CLÍNICO E HISTOPATOLÓGICO E AVALIAÇÃO IMUNOHISTOQUÍMICA DA PROTEÍNA Ki67 EM QUEILITES

Essa tarefa não tem a necessidade de interface com o usuário, tornando-se uma boa candidata ao processamento em lotes, normalmente utilizados como a divisão

Ainda segundo Gil (2002), como a revisão bibliográfica esclarece os pressupostos teóricos que dão fundamentação à pesquisa e às contribuições oferecidas por

Com relação ao CEETEPS, o tema desta dissertação é interessante por se inserir no Programa de Educação de Jovens e Adultos (PROEJA), sob a tutela da Coordenação de

Compreendendo- se que o estudo dos eventos do século XX podem ser relevantes e esclarecedores para a compreensão da história e da sociedade de hoje, e levando-se em conta o