A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Capítulo VIII – Subprogramação

Apresentações semelhantes


Apresentação em tema: "Capítulo VIII – Subprogramação"— Transcrição da apresentação:

1 Capítulo VIII – Subprogramação
8.1 – Introdução 8.2 – Escopo de validade de declarações 8.3 – Parâmetros e passagem de argumentos 8.4 – Prototipação de subprogramas 8.5 – Classes de alocação 8.6 – Recursividade

2 8.3 – Parâmetros e Passagem de Argumentos
8.3.1 – Importância do uso de parâmetros É comum subprogramas atuarem sobre um determinado valor ou uma determinada variável para produzir um resultado ou realizar uma tarefa Exemplos: Calcular o fatorial do valor guardado numa variável Ordenar os elementos de um vetor Trocar os valores de duas variáveis entre si

3 Variáveis globais poderiam ser usadas Nos exemplos anteriores:
O valor cujo fatorial se deseja estaria numa variável global O vetor a ser ordenado seria global As variáveis para a troca de valores seriam também globais Mas, e se forem muitos(as): As variáveis ou as expressões das quais se quer calcular o fatorial? Os vetores a serem ordenados? Os pares de variáveis a trocarem entre si seus valores? Antes de cada chamada, as variáveis globais deveriam ser carregadas com os alvos do subprograma Incômodo!!!

4 Para evitar esse carregamento a cada chamada, usam-se parâmetros e argumentos
Como já foi visto, na chamada de um subprograma, os valores dos argumentos são calculados e armazenados cada um em seu respectivo parâmetro Parâmetro é uma variável local especial de um subprograma, também automática, distinta de suas outras variáveis locais O subprograma então realiza sua tarefa, atuando diretamente sobre seus parâmetros O programa do número de combinações usa parâmetros e argumentos, dispensando variáveis globais

5 8.3.2 – Modos de passagem de argumentos
As linguagens tradicionais de programação apresentam dois importantes modos de passagem de argumentos aos respectivos parâmetros: Passagem por valor Passagem por referência Passagem por valor: o argumento é o valor de uma expressão ou de uma variável, valor esse calculado e carregado no parâmetro Passagem por referência: o argumento deve ser uma variável, sendo que o respectivo parâmetro é alocado coincidindo com o endereço da mesma

6 Exemplo: passagem de argumentos em Pascal
program ppp; var a, b: integer; procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. Procedimento xxx embutido em ppp Comandos do programa ppp A palavra var antes da declaração de y sinaliza que a passagem é por referência

7 Seja a execução deste programa:
program ppp; var a, b: integer; procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. ? a ? b

8 10 a ? a 20 b ? b program ppp; var a, b: integer;
procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 10 a ? a 20 b ? b

9 Chamada da procedure xxx:
program ppp; var a, b: integer; procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 10 a 20 b

10 Alocação dos parâmetros e passagem de argumentos:
program ppp; var a, b: integer; procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 10 x x y 10 a 20 b O valor de a foi copiado em x A variável y foi alocada coincidindo com b

11 10 x x y 10 a 20 b program ppp; var a, b: integer;
procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 10 x x y 10 a 20 b

12 13 x x y 10 a 20 b program ppp; var a, b: integer;
procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 13 x x y 10 a 20 b

13 13 x x y 10 a 20 b program ppp; var a, b: integer;
procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 13 x x y 10 a 20 b

14 13 x x y 10 a 33 b program ppp; var a, b: integer;
procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 13 x x y 10 a 33 b

15 Desalocação dos parâmetros:
program ppp; var a, b: integer; procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. 13 x x y 10 a 33 b

16 a não foi alterada (por valor) b foi alterada (por referência)
program ppp; var a, b: integer; procedure xxx (x: integer; var y: integer); begin x := x + 3; y := y + x; end; a := 10; b := 20; xxx (a, b); write (“a = ”, a, “; b = ”, b); end. a não foi alterada (por valor) b foi alterada (por referência) 10 a 33 b No vídeo a = 10; b = 33

17 Quando se deseja que o argumento sofra alterações dentro do subprograma chamado: usar passagem por referência Quando o subprograma mudar o valor de um parâmetro, mas não se deseja que isso altere o valor do argumento de chamada: usar passagem por valor Quando um subprograma não altera o valor de um parâmetro, pode-se usar os dois tipos de passagem Se o parâmetro for uma variável estruturada (matriz ou estrutura), pode-se economizar memória usando passagem por referência O parâmetro correspondente é alocado numa região já utilizada pelo programa

18 A Linguagem C só trabalha com passagem por valor
A passagem por referência é simulada por argumentos do tipo endereço e parâmetros do tipo ponteiro Isso é visto logo a seguir

19 8.3.3 – Passagem por valor em C
A função destinada ao cálculo de fatorial vista neste capítulo utiliza passagem por valor O valor do argumento é calculado e depositado no parâmetro Depois da execução, mesmo que função mudasse o valor desse parâmetro, as variáveis envolvidas no cálculo dos argumentos não sofrem alteração

20 Numa chamada de função aparece primeiramente seu nome e, a seguir, entre parêntesis, sua lista de argumentos Essa lista deverá ser vazia se a função não tiver parâmetros, mas o uso dos parêntesis é obrigatório Os argumentos devem ser em mesmo número que os parâmetros e devem ser compatíveis com os mesmos Caso um argumento seja o nome de uma variável, apenas seu valor é transmitido ao parâmetro Depois da execução, o valor dessa variável não terá sofrido nenhuma alteração

21 Exemplo: seja o programa
#include <stdio.h> #include <conio.h> void ff (int a) { a += 1; printf ("Durante ff, a = %d\n", a); } void main ( ) { int a = 5; printf ("Antes de ff, a = %d\n", a); ff (a); printf ("Depois de ff, a = %d\n", a); printf("\n\Digite algo para encerrar: "); getch(); Resultado Antes de ff, a = 5 Durante ff, a = 6 Depois de ff, a = 5 Digite algo para encerrar:

22 8.3.4 – Passagem por referência em C
Às vezes, é desejável que a variável argumento seja alterada conforme a mudança sofrida pelo parâmetro correspondente Ao invés de passar o seu valor, passa-se o seu endereço A seguir, um programa ilustrativo para trocar os valores de duas variáveis da função main

23 Exemplo: seja o programa Seja sua execução:
#include <stdio.h> #include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); Seja sua execução:

24 Seja sua execução: 3 i 8 j #include <stdio.h>
#include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); Seja sua execução: 3 i 8 j

25 3 i 8 j #include <stdio.h> #include <conio.h>
void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); 3 i 8 j

26 Chamada de trocar e passagem de argumentos:
#include <stdio.h> #include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); Chamada de trocar e passagem de argumentos: aux p q 3 i 8 j p e q receberam &i e &j - são ponteiros

27 aux p q 3 i 8 j #include <stdio.h> #include <conio.h>
void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); aux p q 3 i 8 j

28 aux recebe conteúdo do local apontado por p (i.é, *p)
#include <stdio.h> #include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); 3 aux p q 3 i 8 j aux recebe conteúdo do local apontado por p (i.é, *p)

29 3 aux p q 3 i 8 j #include <stdio.h> #include <conio.h>
void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); 3 aux p q 3 i 8 j

30 *p recebe conteúdo do local apontado por q (*q)
#include <stdio.h> #include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); 3 aux p q 8 i 8 j *p recebe conteúdo do local apontado por q (*q)

31 3 aux p q 8 i 8 j #include <stdio.h> #include <conio.h>
void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); 3 aux p q 8 i 8 j

32 *q recebe conteúdo de aux
#include <stdio.h> #include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); 3 aux p q 8 i 3 j *q recebe conteúdo de aux

33 Desalocação das variáveis de Trocar:
#include <stdio.h> #include <conio.h> void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); Desalocação das variáveis de Trocar: 3 aux p q 8 i 3 j

34 Resultado 8 i 3 j #include <stdio.h> #include <conio.h>
void trocar (int *p, int *q){ int aux; aux = *p; *p = *q; *q = aux; } int main ( ) { int i = 3, j = 8; printf ("Antes de trocar, i = %d; j = %d\n", i, j); trocar(&i, &j); printf ("Depois de trocar, i = %d; j = %d\n", i, j); printf("\n\Digite algo para encerrar: "); getch(); Antes de trocar, i = 3; j = 8 Depois de trocar, i = 8; j = 3 Digite algo para encerrar: Resultado 8 i 3 j

35 Exercícios 8.3: Dado o programa ao lado contendo variáveis globais, locais e funções com passagem de argumentos por valor e por referência, mostrar o que será escrito no vídeo pela sua execução #include <stdio.h> int i = 58, j = 49; void gg (int i, int j, int k, int m) { printf ("%7d%7d%7d%7d\n\n", i, j, k, m); } void ff (int p, int q, int *r, int *s) { int k, m; gg (p, q, *r, *s); k = 100; m = 200; p = -1; q = -2; *r = -3; *s = -4; gg (i, j, k, m); gg (p, q, *r, *s); main () { int i, j, k, m; i = 10; j = 20; k = 30; m = 40; gg (i, j, k, m); { int j, k; i = 1; j = 2; k = 3; m = 4; gg (i, j, k, m); ff (i, j, &k, &m); gg (i, j, k, m);

36 A conjectura de Goldbach diz que todo número inteiro, par, maior que 2, é a soma de dois números primos. Computadores têm sido muito usados para testar essa conjectura e nenhum contra-exemplo foi encontrado até agora. Escrever uma função que receba como argumento por valor um número inteiro positivo e que retorne 1, se tal número for primo, ou então zero, em caso contrário. Escrever um programa principal para comprovar que tal conjectura é verdadeira dentro de um intervalo lido, no campo dos inteiros maiores que 2, usando obrigatoriamente como subprograma a função elaborada no item a deste exercício. Por exemplo, se o intervalo lido for [700, 1100], o programa pedido deve ser produzir no vídeo um resultado semelhante ao do próximo slide

37 No intervalo [ 700, 1100 ], todo número par é a soma de dois primos, a saber:
700 = 702 = 704 = 1098 = 1100 =

38 Capítulo VIII – Subprogramação
8.1 – Introdução 8.2 – Escopo de validade de declarações 8.3 – Parâmetros e passagem de argumentos 8.4 – Prototipação de subprogramas 8.5 – Classes de alocação 8.6 – Recursividade

39 8.4 – Prototipação de Subprogramas
Nos programas em C, funções devem ser declaradas antes de serem invocadas Há uma diferença entre declarar e definir uma função Declarar: dizer o nome da função, o tipo de seus parâmetros e o tipo do valor por ela retornado Definir: programar a função, ou seja, declará-la, estabelecer suas declarações locais e seus comandos Até agora: declaração das funções no ato de sua definição

40 Há situações em que é interessante ou até necessário que uma função seja invocada antes de ser definida É o caso de programas recursivos, que são assuntos do último tópico deste capítulo int F1 (int a, float x) { chamada de F2() } int F2 (float y, char c ) { chamada de F1() Exemplo: recursividade indireta: - F2 invoca F1 - F1 invoca F2 antes da declaração de F2 - O compilador não aceitará - Invertidas as funções, o problema continua

41 Solução: Protótipos para funções
Protótipo de uma função: é a declaração da função feita separadamente de sua definição No ponto em que F1 invoca F2, essa última já está declarada acima Não é necessário colocar os nomes dos parâmetros, mas somente os tipos Na definição, todos os tipos devem ser os mesmos do protótipo int F2 (float, char); int F1 (int a, float x) { chamada de F2() } int F2 (float y, char c ) { chamada de F1() int F1 (int a, float x) { chamada de F2() } int F2 (float y, char c ) { chamada de F1() Forma geral de um protótipo: Tipo Nome (lista dos tipos dos parâmetros);

42 Protótipos para as funções usadas nos programas deste capítulo:
int fat (int); void sss (void); void ff (void); void ff (int); void trocar (int *, int *); Quando a função não tem parâmetros, coloca-se void entre parêntesis Quando o parâmetro é um ponteiro, coloca-se o asterisco ‘*’ depois do tipo

43 Diretivas de pré-processamento Protótipos das funções
Pode-se adquirir o hábito de fazer protótipos para todas a funções auxiliares dos programas Eles podem ser colocados no início, juntamente com as declarações globais Então a ordem das definições das funções pode ser qualquer Programa Diretivas de pré-processamento Declarações globais Funções auxiliares Função main Protótipos das funções

44 Diretivas de pré-processamento Protótipos das funções
Pode-se organizar o programa de forma a colocar primeiramente a função main Depois vêm aquelas invocadas pela main; depois, aquelas invocadas por essas últimas E assim por diante Essa ordenação é interessante ao se utilizar a metodologia top-down para o desenvolvimento de programas Programa Diretivas de pré-processamento Declarações globais Função main Funções auxiliares Protótipos das funções No capítulo sobre ponteiros serão vistos protótipos de funções com parâmetros do tipo variáveis indexadas, estruturas e funções

45 Capítulo VIII – Subprogramação
8.1 – Introdução 8.2 – Escopo de validade de declarações 8.3 – Parâmetros e passagem de argumentos 8.4 – Prototipação de subprogramas 8.5 – Classes de alocação 8.6 – Recursividade

46 8.5 – Classes de Alocação 8.5.1 - Generalidades
Toda variável e função em C tem, além do tipo, outro atributo, relacionado com a forma de ser alocada durante a execução do programa Esse atributo é a classe de alocação

47 Há 4 classes de alocação de variáveis e funções, a saber:
variáveis automáticas, externas, estáticas e em registradores As palavras reservadas para elas são respectivamente auto, extern, static e register. Essas palavras são colocadas antes do tipo, numa declaração de variáveis e função Exemplos: extern int i; static float a; register int j;

48 int a, b; equivale a auto int a, b;
Como já visto, as variáveis automáticas só ocupam espaço na memória quando seu bloco de declaração está no ar Todas as variáveis locais vistas até agora neste capítulo são automáticas Quando não se especifica a classe de uma variável local, o compilador entende que ela é automática Então, como já foi visto: int a, b; equivale a auto int a, b; Por essa razão, a palavra reservada auto é raramente usada nos programas

49 8.5.2 – Variáveis externas Em C, toda variável declarada fora do escopo de qualquer função, ou seja, toda variável global, pertence à classe das variáveis externas Ela ocupa espaço de memória durante toda a execução do programa A palavra extern pode ser usada em sua declaração, mas na maioria dos casos é dispensável Se int a = 1; for uma declaração global então ela equivale a extern int a = 1;

50 Muitas vezes é desejável compilar cada arquivo separadamente
O principal uso da palavra extern se dá em programas divididos em mais de um arquivo Muitas vezes é desejável compilar cada arquivo separadamente Exemplo: seja o programa a seguir, dividido em dois arquivos arq1.c e arq2.c arq1.c arq2.c #include <stdio.h> #include <conio.h> #include "arq2.c" int a = 1, b = 2, c = 3; void main ( ) { printf ("%3d\n", f( )); printf ("%3d%3d%3d\n", a, b, c); getch(); } int f () { extern int a; int b, c; a = b = c = 4; return (a + b + c); }

51 Na compilação separada do arquivo arq2.c, a declaração extern int a;
indica que a variável a é global e está declarada em outro arquivo Para uma compilação conjunta, esta declaração é dispensável arq1.c arq2.c #include <stdio.h> #include <conio.h> #include "arq2.c" int a = 1, b = 2, c = 3; void main ( ) { printf ("%3d\n", f()); printf ("%3d%3d%3d\n", a, b, c); getch(); } int f () { extern int a; int b, c; a = b = c = 4; return (a + b + c); } No vídeo 12

52 A habilidade em compilar arquivos separadamente é importante ao ser escrever grandes programas
O código-fonte desses programas costuma ser organizado em diversos arquivos Cada arquivo pode conter uma ou mais funções, ou somente declarações globais, ou protótipos de funções, etc. Ocorrendo erros de compilação, só os arquivos com erros precisam ser re-compilados Os arquivos corretos são dispensados disso e, sendo numerosos, ganha-se tempo com essa dispensa

53 8.5.3 – Comunicação entre funções
Funções se comunicam entre si através de variáveis globais, parâmetros por valor, parâmetros por referência e valores retornados a Exemplo int a; void main () { int b, c, d; a = 10; b = 20; c = 30; d = ff (b, &c); printf (“- - -”, a, b, c, d); } int ff (int x, int *y) { int z; z = x + a + *y; a = 1; *y = 2; return z; } b c x y z d

54 Variáveis globais: comunicação nos 2 sentidos
Parâmetros por valor: da invocadora para a invocada Parâmetros por referência: comunicação nos 2 sentidos Valores retornados: da invocada para a invocadora a int a; void main () { int b, c, d; a = 10; b = 20; c = 30; d = ff (b, &c); printf (“- - -”, a, b, c, d); } int ff (int x, int *y) { int z; z = x + a + *y; a = 1; *y = 2; return z; } b c x y z d retorno

55 Variáveis globais dificultam a modularidade e portabilidade das funções
Parâmetros e valores retornados são decididamente preferíveis quando isso é desejado Analogia: aparelho de som para automóveis Má portabilidade: com muitos cabos elétricos do carro a ele conectados, haverá dificuldade para retirá-lo e colocá-lo em outro carro Boa portabilidade: se não houver cabos a ele conectados, essa dificuldade é nula

56 Uma função com variáveis globais a ser disponibilizada para a comunidade exige de quem vai utilizá-la: Conhecimento da existência dessas variáveis Adição das mesmas ao conjunto de variáveis globais de seu programa Uma função sem variáveis globais é muito mais simples de ser reutilizada

57 Há casos no entanto, em que o uso de variáveis globais é providencial
Um grande programa com inúmeros subprogramas atuando numa mesma grande base de dados As variáveis dessa base podem ser globais Não haverá a necessidade de, na maioria das chamadas de subprogramas, fazer passagem de argumentos Evita-se transporte desnecessário de informações entre os subprogramas, mesmo que seja apenas de ponteiros

58 Exemplo: um compilador
Programa-fonte (caracteres) Analisador léxico Analisador sintático Analisador semântico Gerador de código intermediário Otimizador de código intermediário Gerador de código objeto while (i < nn) i = i + j; while ( i < nn ) = + j ; Sequência de átomos i nn j int - - - while i < nn = + j Árvore sintática Tabela de símbolos Código objeto load i R1: sub nn JZ R2 JP R2 add j st i J R1 R2: R1: T1 = i < nn JF T1 R2 T2 = i + j i = T2 JUMP R1 R2: R1: T1 = i < nn JF T1 R2 i = i + j JUMP R1 R2: Código intermediário Exemplo: um compilador

59 Programa-fonte (caracteres)
Analisador léxico Analisador sintático Analisador semântico Gerador de código intermediário Otimizador de código intermediário Gerador de código objeto while (i < nn) i = i + j; Seria incômodo e dispendioso o trânsito dos átomos, da árvore sintática, da tabela de símbolos e do código intermediário pelas funções do compilador Código objeto Fica a cargo do programador escolher o melhor modo de comunicação entre os módulos de seu programa load i R1: sub nn JZ R2 add j st i J R1 R2:

60 8.5.4 – Variáveis em registradores
A Linguagem C permite que o programador expresse sua preferência por registradores, na alocação de uma variável Registradores da CPU são de acesso muito mais rápido que o das palavras da RAM Assim, os programas poderão ficar mais rápidos A declaração register int i; indica que a variável inteira i deve, se possível, ser alocada num registrador

61 Há limitação para o uso de registradores nos programas
Devido à sua sofisticada e cara tecnologia, eles são em número muito menor do que o das palavras da RAM Nem todas as variáveis de um grande programa poderão caber no conjunto de registradores de um computador Portanto o programador deve escolher as variáveis mais referenciadas para serem alocadas em registradores Fortes candidatas para essa alocação são as variáveis usadas em controle de laços Variáveis em registradores, tais como as automáticas, só permanecem alocadas durante a execução de seu bloco

62 8.5.5 – Variáveis estáticas A declaração static int a;
diz que a variável inteira i é estática Variáveis estáticas podem ser locais ou externas Diferentes propriedades e utilidades têm as variáveis estáticas locais e externas Uma variável estática local tem seu valor conservado entre duas execuções consecutivas de seu bloco de declaração Ao contrário, as variáveis automáticas perdem esse valor

63 Exemplo: seja o programa:
#include <stdio.h> #include <conio.h> int i; void f () { static int a = 0; int b = 5; printf ("a = %3d; b = %3d;", a, b); b = i + 10; printf (" b = %3d;\n", b); a += 3; } void main () { for (i = 1; i <= 10; i++) f (); getch(); Variáveis estáticas locais são de uso privativo de seu bloco de declaração Resultado

64 Variáveis estáticas externas oferecem um importante serviço de privacidade, fundamental para a modularização de programas Seu escopo de validade começa no ponto da declaração e vai somente até o final de seu arquivo Se o programa tiver outros arquivos, essa variável não é visível em suas funções Então, pode-se escrever um módulo com um conjunto de funções que tenham uso privativo de certas variáveis Desse conjunto, uma função pode ser a líder e as outras podem ser suas auxiliares

65 Capítulo VIII – Subprogramação
8.1 – Introdução 8.2 – Escopo de validade de declarações 8.3 – Parâmetros e passagem de argumentos 8.4 – Prototipação de subprogramas 8.5 – Classes de alocação 8.6 – Recursividade

66 8.6 – Recursividade 8.6.1 – Definições matemáticas recursivas
Recursividade é um expediente muito usado para se estabelecer certas definições matemáticas Uma entidade de uma certa classe é definida em função de entidades menores da mesma classe

67 Exemplo 1: seja Sn a soma dos n primeiros inteiros positivos
Para n = 5: 1, para n = 1 n + Sn-1, para n > 1 S5 = 5 + S4 = 5 + (4 + S3) = 5 + (4 + (3 + S2)) = 5 + (4 + (3 + (2 + S1))) = 5 + (4 + (3 + (2 + 1))) = 5 + (4 + (3 + 3)) = 5 + (4 + 6) = = 15

68 Exemplo 2: cálculo de fatoriais
n! = Para n = 5: -1, para n < 0 1, para 0 ≤ n ≤ 1 n * (n-1)!, para n > 1 5! = 5 * 4! = 5 * (4 * 3!) = 5 * (4 * (3 * 2!)) = 5 * (4 * (3 * (2 * 1!))) = 5 * (4 * (3 * (2 * 1))) = 5 * (4 * (3 * 2)) = 5 * (4 * 6) = 5 * 24 = 120

69 Exemplo 3: cálculo do mdc de números não-negativos
mdc (m, n) = Para m = 48 e n = 64: ∞, para m = 0 e n = 0 m, para m > 0 e n = 0 n, para m = 0 e n > 0 mdc (n, m%n), para m e n > 0 mdc (48, 64) = mdc (64, 48) = mdc (48, 16) = mdc (16, 0) = 16

70 8.6.2 – Subprogramas recursivos
Subprograma recursivo: invoca a si próprio direta ou indiretamente Recursividade direta: um subprograma invoca a si próprio em seus próprios comandos Recursividade indireta: um primeiro subprograma invoca outro, que invoca outro, ... , que invoca outro, que invoca o primeiro

71 Exemplo 1: cálculo de fatoriais -1, para n < 0
#include <stdio.h> #include <conio.h> int fat (int n) { int f; if (n < 0) f = -1; else if (n <= 1) f = 1; else f = n * fat(n-1); return f; } void main() { char c; int n; printf ("Calculo do fatorial de n"); printf ("\n\n\tDigite n: "); scanf ("%d", &n); printf ("\n\tFat(%d) = %d", n, fat(n)); printf("\n\nDigite algo para encerrar: "); getch(); Exemplo 1: cálculo de fatoriais -1, para n < 0 n! = 1, para 0 ≤ n ≤ 1 n * (n-1)!, para n > 1

72 Exemplo 2: cálculo de mdc’s ∞, p/ m = 0 e n = 0
#include <stdio.h> #include <conio.h> int mdc (int m, int n) { int r; m = abs(m); n = abs(n); if (m==0 && n==0) r = -1; else if (m == 0) r = n; else if (n == 0) r = m; else r = mdc(n, m%n); return r; } void main() { char c; int m, n; printf ("Calculo do mdc de m e n"); printf ("\n\n\tDigite m e n: "); scanf ("%d%d", &m, &n); printf ("\n\tmdc(%d, %d) = %d", m, n, mdc(m, n)); printf("\n\nDigite algo para encerrar: "); getch(); Exemplo 2: cálculo de mdc’s ∞, p/ m = 0 e n = 0 mdc (m, n) = m, p/ m > 0 e n = 0 n, p/ m = 0 e n > 0 mdc (n, m%n), p/ m e n > 0

73 8.6.3 – Execução de subprogramas recursivos
Quando um subprograma invoca a si mesmo, inicia-se uma nova execução (nova versão) desse subprograma Porém a versão que faz a invocação continua ativa Em programas recursivos, pode haver várias versões ativas de um mesmo subprograma recursivo Cada qual com suas variáveis locais As variáveis de versões diferentes têm o mesmo nome mas são entidades distintas

74 Exemplo: execução da função fat recursiva
#include <stdio.h> #include <conio.h> int fat (int n) { int f; if (n < 0) f = -1; else if (n <= 1) f = 1; else f = n * fat(n-1); return f; } void main() { char c; int n; printf ("Calculo do fatorial de n"); printf ("\n\n\tDigite n: "); scanf ("%d", &n); printf ("\n\tFat(%d) = %d", n, fat(n)); printf("\n\nDigite algo para encerrar: "); getch(); Exemplo: execução da função fat recursiva

75 5 n Valor digitado: 5 #include <stdio.h>
#include <conio.h> int fat (int n) { int f; if (n < 0) f = -1; else if (n <= 1) f = 1; else f = n * fat(n-1); return f; } void main() { char c; int n; printf ("Calculo do fatorial de n"); printf ("\n\n\tDigite n: "); scanf ("%d", &n); printf ("\n\tFat(%d) = %d", n, fat(n)); printf("\n\nDigite algo para encerrar: "); getch(); 5 n Valor digitado: 5

76 5 n f fat – v1 4 n f fat – v2 3 n f fat – v3 120 24 6 1 n f fat – v5 2
#include <stdio.h> #include <conio.h> int fat (int n) { int f; if (n < 0) f = -1; else if (n <= 1) f = 1; else f = n * fat(n-1); return f; } void main() { char c; int n; printf ("Calculo do fatorial de n"); printf ("\n\n\tDigite n: "); scanf ("%d", &n); printf ("\n\tFat(%d) = %d", n, fat(n)); printf("\n\nDigite algo para encerrar: "); getch(); 5 n f fat – v1 4 n f fat – v2 3 n f fat – v3 120 24 6 f = 5*fat(4) f = 4*fat(3) f = 3*fat(2) 1 n f fat – v5 2 n f fat – v4 1 2 f = 1 f = 2*fat(1) 5 n Valor digitado: 5

77 Observação: funções recursivas envolvendo vetores, matrizes e estruturas serão apresentadas no próximo capítulo

78 Exercícios 8.6: Uma importante função teórica conhecida como função de Ackermann tem a seguinte formulação recursiva: Ackermann (m, n) = Escrever uma função inteira recursiva em C que tenha como parâmetros duas variáveis inteiras longas m e n e que calcule e retorne o valor de Ackermann (m, n), utilizando a definição acima. Tal função deve retornar -1 (menos 1) se o valor de m ou de n forem negativos


Carregar ppt "Capítulo VIII – Subprogramação"

Apresentações semelhantes


Anúncios Google