• Nenhum resultado encontrado

AulaLab11

N/A
N/A
Protected

Academic year: 2021

Share "AulaLab11"

Copied!
7
0
0

Texto

(1)

1 É mais difícil comandar do que

obedecer. F. Nietzsche.

PASSAGEM DE PARÂMETROS

C

omo visto no laboratório 9, uma função é composta por quatro partes distintas:

• Nome da função

• Tipo de retorno

• Lista de parâmetros

• Corpo da função

PT1: Criar um programa que possui uma

função Maior cuja lista de parâmetros contém dois números inteiros a e b e que retorna o resultado de max{a,b}.

#include <stdlib.h> #include <stdio.h>

int Maior(int a, int b)

{ return (a > b) ? a:b; } 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); }

A função Maior do PT1 possui as seguintes propriedades:

Nome da função Maior Tipo de retorno int Lista de parâmetros int a, int b

Corpo da função return (a>b)? a:b;

Observe que o número e o tipo dos argumentos passados à função devem corresponder ao número e ao tipo dos parâmetros com que esta foi definida. Uma observação importante é que uma função que não retorne qualquer tipo, isto é que retorne void, denomina-se de procedimento.

PE1: Modifique a função Maior do PT1

para que ao invés de retornar o maior entre dois valores, apenas imprima na tela o maior valor, e portanto tenha tipo de retorno igual a void.

PASSAGEM POR VALOR

A

troca de valores entre variáveis é um problema freqüente de programação e pode ser usado na ordenação de um vetor. Uma troca é realizada em três passos e sempre com o auxílio de uma variável auxiliar.

PT2: Crie um programa com uma função

troca que realiza a troca de valores entre duas variáveis.

#include <stdio.h>

void troca(int a, int b); // protótipo

main() { int a, b;

puts(“Dois nums inteiros: ”); scanf(“%d %d”, &a, &b); printf(“Antes de troca \n”); printf(“a= %d e b=%d \n”,a,b); troca(a,b);

printf(“Depois de troca \n”); printf(“a= %d e b=%d \n”,a,b); }

void troca(int a, int b)

{ int tmp; tmp = a; a = b; b = tmp; }

Observe que no PT2 não é realizada a troca de valores. Para entender o que aconteceu, é necessário compreender dois conceitos:

• Escopo de variáveis.

• Passagem por valor e referência.

Quando a função main() é executada, são criadas as variáveis a e b. Por terem sido declaradas na função main() sua visibilidade (ou seja, o escopo para se manipular estas) é restrita à esta função. Quando a função

troca() é invocada, novas variáveis a e b,

visíveis apenas dentro da função troca(), são criadas, suspendendo temporariamente a visibilidade de a e b criadas em main(). A chamada de troca() em main() porém, resulta na passagem da cópia dos valores de a e b de main() para as novas variáveis a e b que são criadas em troca(). Esta situação corresponde ao seguinte esquema:

Função main() Função troca() a = 1; a = 1;

b = 4; b= 4; troca(a,b); comandos;

a=1; a = 4;

b=4; b = 1;

Assim, dentro da função troca() quaisquer alterações de a e b não resultarão em modificações de a e b em main(), pois o que está sendo alterado é apenas uma cópia.

(2)

2

Ou seja, o PT2 corresponde a um programa onde é realizada a passagem de parâmetros por valor, sendo enviadas para a função cópias dos valores originais.

PASSAGEM POR REFERÊNCIA

U

ma forma alternativa é enviar parâmetros por referência, ou seja, o que é enviado para a função não é uma cópia do valor da variável e sim uma referência a esta. Assim, qualquer alteração dos parâmetros realizada na função corresponde a uma alteração nas variáveis originais. Para realizar a passagem por referência ponteiros devem ser utilizados. A estratégia a ser adotada é: (1)Passar o endereço de uma variável, obtido através do operador & e armazenado em uma variável do tipo ponteiro.

(2) Dentro da função, usar ponteiros para alterar os locais para onde eles apontam e que correspondem aos valores das variáveis originais.

PT3: Reescrever o PT2 utilizando a

passagem de parâmetros por referência.

#include <stdio.h>

void troca(int *a, int *b); // protótipo

main() { int a, b;

puts(“Dois nums inteiros: ”); scanf(“%d %d”, &a, &b); printf(“Antes de troca \n”); printf(“a= %d e b=%d \n”,a,b); troca(&a,&b);

printf(“Depois de troca \n”); printf(“a= %d e b=%d \n”,a,b); }

void troca(int *a, int *b)

{ int tmp; tmp = *a; *a = *b; *b = tmp; }

Note que a variável tmp continua a ser declarada do tipo int, pois seu propósito é armazenar um dos valores e não endereço.

PASSAGEM DE VETORES

D

iferentemente das variáveis em que existe a passagem por valor ou referência, a passagem de vetores para funções é sempre por referência. A sintaxe de passagem de um vetor pode ser feita com:

tipo nome[];

onde: tipo – corresponde ao tipo dos elementos do vetor, nome – é o nome atribuído ao vetor e [] indica que a variável é do tipo vetor. O “[]” pode ser utilizado sem um valor, pois em C não interessa qual a dimensão do vetor que é passado a uma função, mas sim o tipo dos seus elementos.

PT4: Crie uma função inic() que inicia um

vetor de inteiros e teste a mesma.

#include <stdio.h>

void inic(int s[], int n)

{ int i; for (i=0;i<n;i++) s[i]=0; } printV(int t[], int n) { int i; for(i=0; i < n; i++) printf(" [%d] ",t[i]); printf("\n"); } main() { int v[10], i; inic(v,10);

// Impressao apos inicializacao.

printV(v,10); // Mudando valores de v. for(i=0;i<10;i++) v[i]=i;

// Impressao apos atribuicao.

printV(v,10); }

Note que a função void inic(int s[], int n) recebe um vetor de inteiros (sem indicar qual a sua dimensão) e um inteiro que indica qual o número de elementos a iniciar.

PE2: Implemente e teste a função float

max(float v[], int n) que recebe um vetor de números reais e o número de elementos a considerar e cujo retorno é o maior número dentre os n primeiros elementos do vetor. A passagem de vetores com mais de uma dimensão para uma função é realizada indicando no cabeçalho desta, obrigatoriamente, o número de elementos de cada um das n-1 dimensões à direita.

(3)

3

Apenas a dimensão mais à esquerda pode ser omitida, colocando-se [] ou um asterisco. É, no entanto, habitual colocar todas as dimensões dos vetores.

PT5: Construir uma função initM() que

inicializa uma matriz e outra função mostraM() que mostra os elementos de uma matriz.

#include <stdio.h> #define DIM 3

void initM(int s[ ][DIM]) // sem 1 DIM

{ int i, j; for (i=0;i<DIM;i++) for (j=0; j <DIM;j++) s[i][j] = 0; }

void mostraM(int s[DIM][DIM]) // 2DIM

{ int i, j;

for (i=0; i<DIM;i++) { for (j=0;j<DIM;j++) printf(“ [%d] ”,s[i][j]); printf(“\n”); } } main() { int s[DIM][DIM]; initM(s); mostraM(s); }

PE3: Adicione uma nova função modEle ao PT5 cujos parâmetros são s, i, j e num, tal

que modifica o elemento s[i][j] com o valor num. Teste para i =1, j = 2 e num = 10.

PE4: Modifique o PT5 para que a função

mostraM ao invés de mostrar os valores inteiros contidos em s, mostre os caracteres associados a cada valor inteiro positivo.

Dica: use uma conversão explícita com

(char)s[i][j] e mude a função initM para inicializar os elementos de s com o valor 1. Uma outra forma de manipular os valores contidos em um vetor através de funções é utilizando ponteiros. Para tanto, basta lembrar que se v for um vetor ou um ponteiro para o primeiro elemento de um vetor (lembre-se que p = v ↔ p = &v[0]), então o elemento cujo índice é i pode ser obtido usando v[i] ou *(v+i) . Ou seja:

v[i] ↔ *(v+i)

Isto só é possível, pois quando um vetor é declarado, os seus elementos são alocados em espaços vizinhos de memória:

EXEMPLO E1:

char v[10] = “Ola”;

v v+1 v+2 v+3

‘O’ ‘l’ ‘a’ ‘\0’

1000 1001 1002 1003

É importante ter em mente que sempre que uma função é invocada utilizando um vetor como um parâmetro, o vetor não é recebido em sua totalidade, mas apenas o endereço inicial do vetor, afinal v ↔ &v[0].

Por esse motivo, o vetor que é definido no cabeçalho de uma função pode ser referenciado através de um ponteiro:

PT6: Construir e testar uma função que

fornece o tamanho de uma String.

#include <string.h> #include <stdio.h>

int strlen(char *s)

{ char *ptr = s; // guardar endereço inicial

while (*s != ‘\0’) // contar até o fim s++;

return (int) (s-ptr); }

main()

{ char Nome[100];

printf(“Digite uma String: ”); gets(Nome); printf(“length = %d \n”,strlen(Nome)); }

Utilizando o Exemplo E1, observe que ao final do while da função strlen o valor de s será 1003 ao passo que ptr terá o valor 1000. Ao se fazer (s-ptr) o resultado é 3, o que corresponde ao tamanho da String (lembre que 1 caractere ocupa 1 byte na memória).

PE5: Modifique strlen() tal que seja

impresso o valor do endereço de memória de ptr e de s antes e depois do while da função strlen().

PT7: Adicionar ao PT6 e testar uma função char * strcpy(char *dest, char *orig) que

copia o conteúdo da string orig na string

dest.

char *strcpy(char *dest, char *orig)

{ char *tmp = dest; while (*dest = *orig) { dest++;

orig++; } return tmp; }

(4)

4

main() { int i;

char N1[100], N2[100], *N3; printf("Digite a String1: "); gets(N1); printf("length = %d \n",strlen(N1)); printf("Digite a String2: "); gets(N2); printf("length = %d \n",strlen(N2)); Nome3 = strcpy(N1,N2);

for(i = 0; i < strlen(N2); i++) printf("%c",*(N3+i)); printf("\n");

}

Observe que o while da função strcpy() só irá parar quando o caractere ‘\0’ que indica fim da string for copiado.

Observe, ainda, que no main() do PT3 a chamada a função foi troca(&a,&b), mas no main do PT7 foi utilizado strcpy(N1, N2). Ou seja, em uma utilizou-se o operador de endereço & e noutra não. Segue um resumo de quando empregar endereços de variáveis na passagem de parâmetros para funções:

• Se a variável for um vetor, seu nome corresponde ao endereço do seu primeiro elemento.

• Se a variável não for um vetor, então, caso se queira alterar a mesma, ela deve ser precedida de & ao se invocar a função.

Estes conceitos estão resumidos no PT8.

PT8: Construir e testar uma função Calc

cuja lista de parâmetros tem um vetor e retorna o valor mínimo e máximo dos seus elementos.

#include <stdio.h>

void Calc(float *v, int num, float *xmin, float *xmax)

{ int i;

*xmin = *xmax = *v; // Valor inicial v[0]

for (i=0; i<num; i++) { if (v[i]<*xmin) *xmin=v[i]; if (v[i]>*xmax) *xmax=v[i]; } } main() {float Vetor[]={10,20,30,40,50,11,125,-33}; float Maior, Menor;

Calc(Vetor, 9, &Menor, &Maior); printf("Maior Elemento %f\n",Maior); printf("Menor Elemento %f\n",Menor); }

ALOCAÇÃO DINÂMICA

N

o laboratório 10 foi visto que a manipulação de ponteiros permite o emprego de alocação de memória para vetores e matrizes em tempo de execução (alocação dinâmica de memória). A alocação dinâmica de memória pode ser empregada em conjunto com a passagem por referência de variáveis nas funções. O PT9 ilustra isso.

PT9: Refazer o PT8 empregando alocação

dinâmica de memória para o vetor v e preenchendo v com valores aleatórios entre 1 e 6 através de uma função void preencheV(int *V, int n, int a, int b), onde: a é o parâmetro do menor valor (1) e b é o parâmetro do maior valor (6).

#include <stdio.h> #include <time.h>

void Calc(float *v, int num, float *xmin, float *xmax);

void preencheV(float *V, int n, int a, int b); void printV(float *V, int n);

main()

{float *V, Maior, Menor; int n; printf(“Tamanho de V:”); scanf(“%d”,&n); V = (float *) calloc(n,sizeof(float)); preencheV(V, n, 1, 6); printV(V,n);

Calc(V, n, &Menor, &Maior);

printf("Maior Elemento %f\n",Maior); printf("Menor Elemento %f\n",Menor); free(V); }

void printV(float *V, int n)

{int i;

for (i=0; i < n; i++) printf(“[%f]”,*(V+i)); printf(“\n”); }

void preencheV(float *V, int n, int a, int b)

{ int i; srand(time(0)); for (i=0; i < n; i++)

*(V+i) = rand()%(b-a+1) + a;}

void Calc(float *v, int num, float *xmin, float *xmax)

{ int i;

*xmin = *xmax = *v; // Valor inicial v[0]

for (i=0; i<num; i++) {if (v[i]<*xmin) *xmin=v[i]; if (v[i]>*xmax) *xmax=v[i]; } }

(5)

5

MATRIZES

N

o laboratório 10 foi visto que uma variável do tipo ponteiro pode servir para manipular os elementos de um vetor, ou ainda, funcionar como um vetor. É possível, ainda, utilizar ponteiros para manipular os elementos de uma matriz. Para tanto, será necessário empregar ponteiros de ponteiros. O Exemplo E2 e o PT10 ilustram como empregar ponteiros de ponteiros para se manipular os elementos de uma matriz. EXEMPLO E2:

float **M;

// Constrói vetor de m posições capaz de // armazenar m elementos do tipo float *.

M = (float **) calloc(m,sizeof(float *));

for (i=0; i < m; i++)

// Cada elemento M[i] possui um // endereço que representa o primeiro // elemento de um vetor de tamanho n.

M[i] = (float *) calloc(n,sizeof(float *)); M[0] M[1] M[2] 5000 6000 7000 8000 8004 8008 M[0][0] M[0][1] M[0][2] M[0][3] 1.0 2.0 3.0 4.0 5000 5004 5008 5012 M[1][0] M[1][1] M[1][2] M[1][3] 5.0 6.0 7.0 8.0 6000 6004 6008 6012 M[2][0] M[2][1] M[2][2] M[2][3] 9.0 10.0 11.0 12.0 7000 7004 7008 7012

PT10: Gerar aleatoriamente uma M (3 x 4)

cujos elementos são valores aleatórios entre 1 e 12. Construa M utilizando alocação dinâmica de memória.

#include <stdio.h> #include <time.h>

void preencheM(float **M, int m, int n, int

a, int b);

void printM(float **M, int m, int n);

main() {float **M; int i, m, n;

printf(“Linha e Coluna de M:”); scanf(“%d%d”,&m, &n);

M = (float **) calloc(m,sizeof(float *));

for (i=0; i < m; i++)

M[i] = (float *) calloc(n,sizeof(float)); preencheM(M, m, n, 1, 12);

printM(M, m, n); free(M); }

void printM(float **M, int m, int n)

{int i, j;

for (i=0; i < m; i++) {

for (j=0; j < n; j++)

printf(“ [%5.2f] ”,*(*(M+i)+j)); printf(“\n”); } }

void preencheM(float **M, int m, int n, int

a, int b)

{ int i, j; srand(time(0)); for (i=0; i < m; i++) for (j=0; j < n; j++)

M[i][j] = rand()%(b-a+1) + a;}

Observe que no PT10 a função preencheM utiliza a notação de ponteiros para acessar os elementos da matriz M: *(*(M+i)+j). Para ilustrar o funcionamento desta notação, imagine que i = 1 e j = 2 e use o esquema descrito no Exemplo E2. Assim: *(*(M+i)+j)= *(*(8000+i)+j)= *(*(8004)+j) = *(6000+j)= *(6008)= 7.0. Note que o mesmo valor de M seria obtido se fossem usados índices de matriz, ou seja, M[1][2]. A partir deste fato, a função preencheM usa a notação com índices M[i][j] para preencher a matriz M.

PE6: Modifique o PT10 para mostrar o

maior e o menor valor da matriz M através de uma função void Calc(float **M, int m,

int n, float *xmin, float *xmax).

NOVOS TIPOS

N

o laboratório 9 foi visto como definir novos tipos de variáveis. Pode-se construir alocar dinamicamente vetores cujos elementos são novos tipos tal como ilustrado no PT11:

PT11: Preencher e imprimir um cadastro

utilizando um vetor do tipo PESSOA.

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

(6)

6

// Protótipos das funções.

void preencheVP(PE *V, int n); void printVP(PE *V, int n);

main() { PE *V; int n; printf(“Tamanho de V:”); scanf(“%d”,&n); V = (PE *) calloc(n,sizeof(PE)); preencheVP(V, n); printVP(V,n);

// Liberando memória alocada para V.

free(V); }

// Impressão de um vetor cujos // elementos são do tipo PE.

void printVP(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”); }

// Preenchendo um vetor cujos // elementos são do tipo PE.

void preencheVP(PE *V, int n)

{ int i;

for (i=0; i < n; i++) {

printf(“Cadastro %d \n”, i+1); printf(“Entre com o nome: ”); // *(V+i). nome equivale a usar // (V+i)->nome !

fflush(stdin); gets((V+i)->nome);

printf(“Entre com salario: ”); scanf(“%f”,&((V+i)->salario)); printf(“Entre com sexo: ”); (V+i)->sexo = getche(); fflush(stdin);

system(“CLS”); }

}

No PT11 a função printVP é responsável por imprimir cada campo de cada elemento do vetor preencheVP. Para tanto, é utilizado a notação de ponteiro (*(V+i)).nome para imprimir a informação do nome do i-ésimo cadastro. Já a função preencheVP é responsável por armazenar a informação do cadastro de uma pessoa, mas o faz utilizando outra notação, (V+i)->nome, para acessar o campo nome do i-ésimo elemento de V.

PROGRAMAS BÁSICOS

PB1: Crie e teste a função void Troca3(int a, int b, int c) que retorna a = c, b = b e c = a.

Dica: Uma forma de resolver o problema é reimplementar o PT3, e outra é realizar chamadas à função troca dentro da função Troca3.

PB2: Generalize o PB1, criando e testando a

função void Inversão(int v []) que inverte a posição que os elementos ocupam em um vetor v.

Exemplo:

v = {20, -40, 50, 70} v = {70, 50, -40, 20}

PB3: Teste e crie uma função que dado um

vetor v, imprime seus elementos na forma de coeficientes de um polinômio.

Exemplo:

Nome do polinômio: P Grau do polinômio: 3

Entre com os coeficientes: 20 -40 50 70 P(x) = 20 -40*x + 50*x^2 + 70*x^3

PB4: Implemente e teste a função void

transpor(int v[VMAX][VMAX]) que transpõem a matriz v com MAX por MAX elementos.

PB5: Reimplemente e teste o PB2 e o PB4

usando ponteiros.

PB6: Escreva uma versão iterativa e outra

recursiva de uma função int Fib(int n) que retorna o n-ésimo termo da seqüência de Fibonacci dada por:

     − + − = = = c.c. ), 2 ( ) 1 ( 1 se , 1 0 n se , 0 ) ( n f n f n n f

PB7: Usando o PB6, escreva uma função void seqFib(int v[], int n) que retorna os n

primeiros termos da seqüência de Fibonacci no vetor v.

(7)

7

PB8: Reimplemente o PB7 utilizando

ponteiros.

PB9: Teste a função misterio com o

seguinte código: char *misterio(char *s) { if (*s == ‘\0’) putchar(‘\n’); else { putchar(*s); misterio(s+1); } }

Diga o que ela faz e qual comando em C é equivalente.

PB10: Reimplemente o PB4 usando

ponteiros para passagem da matriz v como parâmetro da função transpor().

PROGRAMAS AVANÇADOS

PA1: O código de PT5 pode ser modificado

para se criar um Jogo da velha da seguinte forma:

#include <stdio.h> #define DIM 3 #define ESPACO ‘ ’

void inic(char s[][DIM])

{ int i, j;

for (i=0; i < DIM; i++) for (j=0; j < DIM; j++) s[i][j]= ESPACO; }

void mostra(char s[DIM][DIM])

{

int i, j;

for (i=0; i < DIM; i++)

{

for (j=0; j < DIM; j++)

printf(“%c %c”,s[i][j], j==DIM-1? ‘ ’: ‘ |’ ); if (i != DIM-1) printf(“\n- - - ”); putchar(‘\n’); } } main() { char Velha[DIM][DIM]; int posx, posy;

char ch = ‘0’; // Caractere das jogadas

int n_jogadas = 0; inic(Velha);

while (1) // Laço infinito

{

mostra(Velha);

printf(“\nIntroduza a Posição de Jogo Linha e Coluna”);

scanf(“%d %d”,&posx,&posy); // Índices do vetor começam em 0

posx-- ; posy--; // Posição livre if (Velha[posx][posy] == ESPACO) { Velha[posx][posy] = ch = (ch == ‘0’)? ‘X’ : ‘0’; n_jogadas++; } else

printf(“Posição ja ocupada \n Jogue de novo !! \n”); if (n_jogadas ==DIM*DIM) break; } mostra(Velha); } Pede-se:

Utilizar o comando system("CLS"); para limpar a tela após cada jogada.

• Inserir uma alteração tal que o jogo da velha termine quando algum jogador completar alguma linha, coluna ou diagonal.

PA2: Reescreva PB7 usando alocação

dinâmica de memória para construir o vetor v. Dica: Neste caso, a passagem de parâmetros deve ser realizada utilizando ponteiros.

PA3: Escreva, usando recursão, a função char *strchr(char *str, char ch) que retorna

o endereço em que se encontra o caractere ch na string str. Caso o caractere não exista, na string, então será devolvido NULL.

Referências

Documentos relacionados

Levando-se em consideração o potencial das formas fractais, os modelos de tradução da Geometria Fractal para a arquitetura que evocam esse potencial e os diversos edifícios

Em um sistema de automação onde as mensagens de alarmes exibidas no EMS são padronizadas é possível utilizar este padrão para identificar os alarmes de interesse ao diagnóstico

2.2.3 Efeitos prejudiciais do fitato Embora a presença do fitato seja desejada para a maioria dos alimentos vegetais, por ser uma importante fonte de fósforo e outros

eu vivo aqui na Inglaterra, mas a gente estava de férias na Somália e eu tive a MGF. Eu fui educada, nunca minha família colocou limites nas minhas ambições e eu acho que foi por

Dessa forma, é importante aproveitar a atenção realizada no pré-natal para nortear a mulher e sua família e incentivar sua autonomia e empoderamento no processo

Consultar a Secção 8 para informações sobre o equipamento de protecção individual apropriado.. Consultar a Secção 13 para mais informações sobre tratamento

Ato contínuo, Pareceres foram votados no Plenário, houve análise das emendas do Senado Federal pela Câmara dos Deputados, por fim, o texto final foi encaminhado para avaliação

x%B"0%w m*@0!"+"%6#qonsˆxt vDvrnwßÞu ppvDrnwßm nooDrÕwnu vDnÕpßßru nDwooqvnu ßrDqwpßrm >múg>-I5ªuvÕo vDpÞÞrÕu ppwDoqÞm norDÞowvm vDnßoÞru nDvÞÞvvpu