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

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

Laboratório de Programação

Apresentações semelhantes


Apresentação em tema: "Laboratório de Programação"— Transcrição da apresentação:

1 Laboratório de Programação
Prof. Oscar Luiz Monteiro de Farias

2 Capítulo 4 Funções e Estrutura de Programa

3 Funções Permitem a divisão de grandes tarefas computacionais em tarefas menores. Possibilita o uso de código anteriormente desenvolvido por terceiros. Essencial no desenvolvimento de sistemas mais complexos. Ocultam o código da implementação, facilita alterações. Um programa em C consiste de várias funções. Pode residir em um ou mais arquivos fontes. Podem ser compilados em separado e carregados juntos, com funções de bibliotecas previamente compiladas. Uso de protótipos / coerção automática de tipos

4 Funções - prog32-chap04-pg60.c (1)
Desenvolver um programa para imprimir cada linha de entrada que contenha um padrão (cadeia de caracteres específica). Por examplo, o padrão ``ould'' no conjunto de linhas de entrada: Ah Love! could you and I with Fate conspire To grasp this sorry Scheme of Things entire, Would not we shatter it to bits -- and then Re-mould it nearer to the Heart's Desire! Produziria o seguinte resultado:

5 Funções - prog32-chap04-pg60.c (2)
A tarefa divide-se em três partes: while (há outra linha) if (a linha contém o padrão) imprima-a Estas tarefas são mapeadas em três funções: - while (há outra linha) → getline (ver chap01) - imprima-a → printf (standard library stdio.h) - a linha contém o padrão → strindex(s,t) que retorna a posição ou índice, no string s, aonde o string t se inicia, e -1 se s não contém t.

6 Funções - prog32-chap04-pg60.c (3)
Main retorna o número de padrões encontrados, para o ambiente que executa o programa (found). Quando, mais tarde, precisarmos de um código de pattern matching mais sofisticado precisaremos substituir apenas a função strindex. A biblioteca padrão do C possui a função strstr que é similar a strindex, exceto que retorna um pointer ao invés de um índice. No chap05 veremos como passar o padrão como um parâmetro para o programa.

7 Funções - definição Forma geral de definição de uma função:
tipo-retornado nome-da-função (lista de declarações de parâmetros) { declarações statements } As definições de funções podem aparecer em qualquer ordem em um único arquivo fonte ou em arquivos diversos.

8 Função minimalista void dummy() {}
Esta função não faz nada e não retorna valor algum. Pode ser usada como um place-holder, durante o desenvolvimento de programas. Um programa em C é um conjunto de definições de funções e variáveis. A comunicação entre as funções se dá por: argumentos e valores retornados pelas funções e variáveis externas.

9 Funções (3) Protótipo de função: declaração que deve combinar com a definição da função. Envolve apenas tipo-retornado nome-da-função (lista de declarações de parâmetros); Parâmetro (argumento formal): variável nomeada na lista entre parênteses em uma definição de função. Argumento (argumento real): variável ou valor usado na invocação (chamada) de uma função.

10 Funções (4) Os nomes dos parâmetros e argumentos não precisam ser idênticos. Os nomes dos parâmetros são opcionais no protótipo da função. A função retorna o seu valor (que é de um tipo especificado) por meio de um comando. return expressão; A expressão será convertida para o tipo retornado pela função, se necessário. Uma função não precisa retornar um valor. Neste caso, o tipo-retornado é void.

11 Funções (5) return; faz com que o controle, mas não um valor, seja retornado ao trecho de código que invocou a função. Equivalente a chegar a }, o delimitador de fim da função. main é uma função e pode possuir um comando return ao seu final (retorna um valor ao ambiente em que o programa em C foi executado. Código de retorno 0 → término normal. Código de retorno != 0 → indica situações incomuns ou erros.

12 Funções (6) Diferenças da definição e declaração de funções entre o ANSI C e as versões anteriores de C (ver prog13a-chap01-pg28.c). Os parâmetros são nomeados entre parênteses -( ) - e os seus tipos são declarados antes do abre- chave - { . No protótipo da função não era permitido a declaração dos tipos de parâmetros (resultado: o compilador não tinha como checar se a invocação da função estava correta).

13 Funções - call by value (2)
Call by value é uma vantagem → propicia o encapsulamento do código da função, evitando reflexos nas variáveis do código que invoca a função. Os parâmetros comportam-se como variáveis locais à função que foi invocada. São inicializados com os valores assumidos pelos argumentos no trecho do código que invocou a função (ver prog14-chap01-pg28.c).

14 Funções - call by reference
Em C, quando necessário, é possível a uma função, alterar o valor de uma variável na função que a invoca. Na invocação da função deve-se fornecer, como argumento, o endereço da variável a ser alterada (pointer para a variável). A função invocada deve declarar, como parâmetro um pointer e deve referenciar indiretamente o valor da variável.

15 Funções – vetores como argumentos
Quando o nome do vetor é usado como argumento, o valor passado à função é o local ou endereço de início do vetor. Não há uma cópia para os elementos do vetor. A função pode ter acesso e alterar qualquer elemento do vetor com o uso de subscritos.

16 Compilação e carga A compilação e carga de um programa em C residente em múltiplos arquivos fontes varia de um sistema para outro. Normalmente permite, com um único comando, compilar arquivos fontes residentes em arquivos diferentes. Exemplo: gcc main.c getline.c strindex.c (Ubuntu) Supondo que as funções do programa anterior estivessem armazenadas em três arquivos diferentes. Irá compilar os três arquivos, colocando o código objeto nos arquivos main.o, getline.o e strindex.o, carregando-os em um único arquivo executável denominado a.out. Se houver um erro, digamos em main.c, este poderá ser compilado em separado, e o resultado carregado junto com os outros arquivos objetos. gcc main.c getline.o strindex.o gcc -o myexec main.c getline.c strindex.c colocará o código executável no arquivo myexec.

17 gcc compiler & makefile
A Simple Makefile Tutorial Geany - User Guide to configuring the Build Menu Make (software) GNU `make'

18 Funções - tipos retornados (1)
Exemplo de função que retorna um outro tipo, que não int e void. A função atof(s), que converte uma cadeia de caracteres (string) s em seu equivalente em ponto flutuante de dupla precisão. atof é uma extensão de atoi (ver chaps 02 e 03). atof trata um sinal e ponto decimal opcionais e a presença ou ausência de uma parte inteira ou fracionária. A biblioteca padrão inclui uma função atof. O arquivo header <stdlib.h> a declara.

19 Funções - tipos retornados (2)
atof → prog33-chap04-pg62.c Primeiro, atof deve declarar o tipo que retorna (double), pois este não é int (default). Segundo, a função que invoca atof deve registrar que atof retorna um valor double. Uma possibilidade é declarar atof explicitamente na função chamadora. double sum, atof(char []); atof deve ser declarada e definida de forma coerente.

20 Funções - tipos retornados (3)
A definição da função e a sua invocação devem possuir tipos coerentes, em caso contrário, se estiverem no mesmo arquivo, um erro será assinalado pelo compilador. Se estiverem em arquivos diferentes, seriam compilados em separado e uma evental discordância entre os tipos não seria detectada em tempo de compilação. P.ex., atof poderia retornar um double, que main trataria como um int e isto geraria respostas possivelmente sem significado.

21 Funções - tipos retornados (4)
Motivos de discordância: i) se não houver um protótipo de função, uma função é implicitamente declarada pela sua primeira aparição em uma expressão, como em: sum += atof(line) Se um nome, que não foi previamente declarado, aparecer em uma expressão e for seguido por um abre parênteses, ele é declarado pelo contexto como sendo um nome de função e o compilador assume int como o tipo retornado pela função.

22 Funções - tipos retornados (5)
Motivos de discordância: ii) se a declaração de uma função não incluir argumentos, como em double atof(); presume-se que nada deve ser assumido sobre os argumentos de atof. Toda a verificação de parâmetros é desativada. Este sinificado especial da lista de argumentos vazia permite que programas mais antigos sejam compilados nos compiladores mais modernos. Assim, se uma função possui argumentos, declare-os, se não possui argumentos use void.

23 atoi em termos de atof /* atoi: convert string s to integer using atof */ int atoi(char s[]) { double atof(char s[]); return (int) atof(s); } O valor da expressão em return expression; é convertido para o tipo retornado pela função, antes que o retorno seja efetuado. O valor retornado por atof seria automaticamente convertido para int (potencialmente descartando informação). O compilador emitiria um warning. O type cast (molde) informa explicitamente que o descarte é feito e omite o warning.

24 Variáveis externas (1) Um programa em C consiste de um conjunto de objetos externos: funções e variáveis. Variáveis externas são definidas fora de qualquer função. Estão potencialmente disponíveis para diversas funções. Funções são sempre externas, pois C não permite que elas sejam definidas dentro de outras funções.

25 Variáveis externas (2) Por default, variáveis externas e funções têm a propriedade de que todas as referências a elas pelo mesmo nome, mesmo de funções compiladas em separado, são referências à mesma entidade (external linkage). Por serem acessíveis globalmente, são uma alternativa mais conveniente e eficiente para comunicação entre funções com longas listas de argumentos. Todavia, o programa fica mais acoplado.

26 Variáveis externas e escopo (1)
As variáveis declaradas em uma função são privativas ou locais à função. Outras funções não têm acesso a elas (isto vale também para as variáveis declaradas em main). Cada variável local a uma função existe somente enquanto a função está ativa e deixa de existir quando a função termina. Por isso são também conhecidas como variáveis automáticas.

27 Variáveis externas e escopo (2)
As variáveis automáticas devem ser explicitamente inicializadas a cada chamada. É possível definir-se variáveis externas a todas as funções – as variáveis globais. Elas são visíveis a todas as funções e podem ser por elas alteradas. As variáveis externas são permanentes e retêm os seus valores entre as diversas chamadas a uma função.

28 Variáveis externas e escopo (3)
Tornam o programa mais acoplado, o que dificulta alterações futuras e a própria compreensão do programa. Uma variável externa deve ser definida exatamente uma vez, fora de qualquer função. Isto aloca área de armazenamento para ela. Cada função que desejar ter acesso à variável externa deve declará-la explicitamente com extern ou implicitamente pelo contexto.

29 ex.: calculadora (1) Programa que simula uma calculadora com os operadores: +, -, * e /. Usa a notação polonesa reversa. (1 – 2) * (4 + 5) (infix) é submetida à calculadora como (postorder): 1 2 – * CE ce cd visit * - + 1 2 4 5

30 ex.: calculadora (2) Implementação: cada operando é colocado em uma pilha; quando um operador aparece, o número apropriado de operandos é desempilhado, o operador é aplicado aos mesmos e o resultado empilhado. No exemplo, 1 e 2 são empilhados, aparece o operador -; 1 e 2 são desempilhados e o resultado da operação (1-2) é empilhado. Depois, 4 e 5 são empilhados, aparece o operador +, 4 e 5 são desempilhados e o resultado da operação (4+5) é empilhado. Aparece o operador *, os operandos -1 e 9 são desempilhados e o resultado da operação (-1*9) é empilhado. O valor no topo da pilha é impresso, quando é encontrado o final da linha de entrada.

31 Estrutura do programa (1)
while (next operator or operand is not end-of-file indicator) if (number) push it else if (operator) pop operands do operation push result else if (newline) pop and print top of stack else error

32 Estrutura do programa (2)
Funções push, pop, getop – para obter o próximo operador ou operando da entrada. main não necessita conhecer as variáveis que controlam a pilha. Deve pensar apenas em termos de pop e push. A pilha e sua informação associada foram declaradas como variáveis externas, acessíveis a pop e push, mas não à main. O programa residirá em um único arquivo fonte.

33 Estrutura do programa (3)
#includes #defines function declarations for main main() { ... } external variables for push and pop void push( double f) { ... } double pop(void) { ... } int getop(char s[]) { ... } routines called by getop

34 Estrutura do programa (4)
A função main é um loop, contendo um switch no tipo de operador ou operando - prog35-chap04-pg65.c. + e * são operadores comutativos, assim a ordem em que os operandos é desempilhada é irrelevante. Mas repare que em C a ordem em que chamadas a funções são avaliadas não é definida. push(pop() - pop()); /* ERRADO */ Para garantir a ordem correta é necessário desempilhar o primeiro operando para uma variável temporária.

35 Estrutura do programa (5)
void push(double f) - prog36-chap04-pg66.c double pop(void) Uma variável é considerada extern se for definida fora de qualquer função. Assim a pilha e seu índice, que devem ser compartilhados por push e pop, são definidos for a dessas funções. int getop(char s[]) - prog37-chap04-pg66.c Pule espaços e tabulações. Se o próximo caractere não for um dígito ou ponto decimal, retorne-o. Caso contrário, agrupe uma cadeia de dígitos (que pode incluir um ponto decimal) e retorne NUMBER, o sinal de que um número foi capturado.

36 Estrutura do programa (6)
int getch(void) - prog38-chap04-pg67.c void ungetch(int c) getch obtém o próximo caracter de entrada. ungetch devolve um caractere para a entrada, de modo que a próxima invocação a getch o retorne. Ao varrer a entrada procurando um número só sabemos que chegamos ao final do número ao nos depararmos com o primeiro caracter que não faz parte do número. Porém ele precisa ser analisado. Daí ungetch(c)... O programa na íntegra prog39-chap04- PROGS all-together.c

37 Regras de escopo (1) As funções e variáveis externas que integram um programa em C não precisam estar todas em um único arquivo. O texto fonte do programa pode estar distribuído por vários arquivos. Podem ser carregadas funções previamente compiladas das bibliotecas. O escopo de um nome é a parte do programa dentro da qual o nome poderá ser usado. Para as variáveis automáticas declaradas no início de uma função, o escopo é a função onde foram declaradas. Os parâmetros da função também podem ser vistos como variáveis locais.

38 Regras de escopo (2) O escopo de uma variável externa ou de uma função abrange desde o ponto em que foi declarada até o final do arquivo sendo compilado. Por exemplo, se main, sp, val, push, e pop são definidos em um arquivo na ordem que se segue main() { ... } int sp = 0; double val[MAXVAL]; void push(double f) { ... } double pop(void) { ... }

39 Regras de escopo (3) Então as variáveis sp e val podem ser usadas em push e pop simplesmente nomeando-as; Nenhuma outra declaração é necessária. Mas esses nomes não são visíveis em main, assim como push e pop. Se uma variável externa deve ser referenciada antes de sua definição, ou se ela for definida em um arquivo fonte distinto daquele em que está sendo usada, então uma declaração extern é obrigatória.

40 Variáveis externas e escopo
Uma prática comum é inserir a definição de todas as variáveis externas no início do arquivo fonte e omitir as declarações extern dentro das funções. Se o programa está distribuído por vários arquivos fontes e uma variável extern é definida em arquivo1 e usada em arquivo2 e arquivo3, então as declarações extern em arquivo2 e arquivo3 são necessárias. Uma alternativa é concentrar todas as declarações extern em um arquivo separado (header) e incluí-lo por um #include no início de cada arquivo fonte. #include ''myheader.h''

41 Definição de variável Definição – refere-se ao local aonde se cria a variável ou se destina área de armazenamento para a mesma. Se as linhas int sp; double val[MAXVAL]; aparecerem fora de qualquer função, elas definem as variáveis externas sp e val, fazem que áreas de memória sejam a elas destinadas e também servem como declaração para o restante do arquivo fonte.

42 Declaração de variável (1)
Declaração – refere-se ao local em que a natureza da variável é estabelecida (seu tipo), mas não há alocação de área para a mesma. As linhas extern int sp; extern double val[]; declaram, para o restante do arquivo fonte, que sp é um int and que val é um vetor do tipo double (cujo tamanho é determinado em outro local), mas não criam as variáveis ou reservam espaço para elas.

43 Declaração de variável (2)
Deve haver apenas uma definição de variável extern entre todos os arquivos que compõem o programa fonte. Outros arquivos podem conter declarações extern para acessá-la. Também pode haver declarações extern no arquivo contendo a definição. A inicialização de uma variável extern é feita somente em sua definição.

44 Arquivos header Vamos considerar o particionamento do programa calculadora em diversos arquivos fontes. A função main integraria o primeiro arquivo main.c. push, pop, e suas variáveis iriam em um segundo arquivo, stack.c; getop, em um terceiro, getop.c. Finalmente, getch e ungetch integrariam um quarto arquivo, getch.c; Em um programa realista as funções getch e ungetch viriam de uma biblioteca compilada em separado. As definições e declarações compartilhadas entre os arquivos viriam em um arquivo header, calc.h.

45

46 Variáveis estáticas (1)
As variáveis sp e val em stack.c, and buf e bufp em getch.c, são para uso privativo de funções em seus respectivos arquivos fontes e não são para outros terem acesso às mesmas. A declaração static aplicada a uma variável externa ou função, limita o escopo desse objeto ao restante do arquivo fonte sendo compilado. As variáveis externas static fornecem uma forma de esconder nomes como buf e pbuf na combinação getch- ungetch, que devem ser extern, para que possam ser compartilhados, mas que não devem ser visíveis aos usuários de getch e ungetch.

47 Variáveis estáticas (2)
Sintaxe: basta prefixar a declaração com a keyword static. Se as duas funções e as duas variáveis são compiladas em um arquivo como se segue em static char buf[BUFSIZE]; /* buffer for ungetch */ static int bufp = 0; /* next free position in buf */ int getch(void) { ... } void ungetch(int c) { ... } então nenhuma outra função será capaz de ter acesso à buf e pbuf, e esses nomes não entrarão em conflito com nomes idênticos em outros arquivos do mesmo programa.

48 static e funções Normalmente os nomes de funções são globais, visíveis ao programa inteiro. Todavia, se uma função for declarada static, seu nome ficará invisível for a do arquivo onde está sendo declarada. No caso de ser aplicada a variáveis internas/automáticas, elas permanecem em existência independente da ativação ou desativação da função,preservando seus valores.

49 Variáveis em registradores (1)
Variáveis declaradas como register indicam para o compilador que a variável deverá ser colocada em registradores da máquina. Indicado para variáveis de uso intenso. Pode resultar em programas menores e mais rápidos. Mas, os compiladores são livres para ignorar esta diretiva. Formato: register int x; register char c;

50 Variáveis em registradores (2)
A declaração register só pode ser aplicada a variáveis automáticas e aos parâmetros formais de uma função. f(register unsigned m, register long n) { register int i; ... } Somente algumas poucas variáveis em cada função podem ser mantidas em registradores e somente certos tipos de dados são permitidos.

51 Variáveis em registradores (3)
Declarações register em excesso são ignoradas. Não é possível ter-se acesso ao endereço de uma variável register (capítulo 5). As restrições específicas sobre número e tipos de variáveis register variam de hardware para hardware.

52 Estrutura de bloco (1) C não é uma linguagem com estrutura de bloco, como Pascal, Algol, etc., pois funções não podem ser definidas dentro de outras funções. Mas variáveis podem ser definidas seguindo uma estrutura de bloco dentro de uma função. Declarações de variáveis (incluindo inicializações) podem seguir o abrechaves “{“ que inicia qualquer comando composto, e não apenas o que inicia uma função.

53 Estrutura de bloco (2) Variáveis declaradas em um bloco mais interno ocultam variáveis declaradas em blocos mais externos com nomes idênticos e, permanecem em existência, até o fecha-chaves “}” correspondente. if (n > 0) { int i; /* declare a new i */ for (i = 0; i < n; i++) ... }

54 Estrutura de bloco (3) Uma variável automática declarada e inicializada em um bloco é inicializada toda vez em que se entra no bloco. As variáveis static são inicializadas apenas na primeira vez em que se entra no bloco. As variáveis automáticas, incluindo os parâmetros formais, também ocultam variáveis externas e funções de mesmo nome. int x; int y; f(double x) { double y; ... }

55 Inicialização (1) Quando não são inicializadas explicitamente, variáveis extern e variáveis static são inicializadas com o valor zero. Já as variáveis automáticas e em registradores têm valores iniciais indefinidos (i.e., lixo). Variáveis escalares podem ser inicializadas quando são definidas, com uma expressão seguindo-se ao operador de atribuição “=”. int x = 1; char squota = '\''; long day = 1000L * 60L * 60L * 24L; /* milliseconds/day */

56 Inicialização (2) Variáveis extern e static devem ter como inicializadores uma expressão constante. A inicialização ocorre apenas uma vez, conceitualmente ante que o programa se inicie. Para variáveis automáticas (locais) e register ela ocorre toda vez que a função ou bloco é iniciado. Para variáveis automáticas (locais) e register o inicializador não se restringe a uma constante; pode ser qualquer expressão válida envolvendo valores previamente definidos.

57 Inicialização (3) Por exemplo, a inicialização do programa de pesquisa binária na secção 3.3 poderia ser escrita como: int binsearch(int x, int v[], int n) { int low = 0; int high = n - 1; int mid; ... } ao invés de int low, high, mid; low = 0; high = n – 1;

58 Inicialização (4) A inicialização de um vetor é uma lista de inicializadores entre chaves e separados por vírgula e que se segue à sua declaração. int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } Quando se omite o tamanho do vetor, o compilador calculará o seu tamanho contando os inicializadores. Se houver inicializadores em menor número que o tamanho especificado para o vetor, os elementos restantes serão zerados, no caso de variáveis extern e static, e serão preenchidos com lixo, no caso de variáveis automáticas. Não há meios para especificar a repetição de um inicializador, nem inicializar um elemento no meio do vetor, sem fornecer os elementos anteriores.

59 Inicialização (5) Os vetores de caracteres são um caso especial de inicialização. Uma cadeia de caracteres pode ser usada, ao invés da notação com chaves e vírgulas. char pattern = "ould"; é uma abreviação para: char pattern[] = { 'o', 'u', 'l', 'd', '\0' }; Neste caso o vetor possui 5 elementos (deve-se considerar o '\0' ).

60 Recursividade (1) Funções em C podem ser usadas recursivamente, i.e, invocarem a si próprias direta ou indiretamente. Exemplo #1: Impressão de um número como uma cadeia de caracteres. Os dígitos são gerados em ordem contrária. Os dígitos de mais baixa ordem estão disponíveis antes que os de mais alta ordem. Solução #1: armazenar os dígitos em um vetor, à medida em que são gerados, e imprimí-los em ordem inversa (pilha). Solução #2: usar recursividade – prog41-chap04-pg73.c solução falha com o maior número negativo (porquê?)

61 Recursividade (2) Exemplo #2 - Quicksort - algoritmo de ordenação desenvolvido por C.A.R.Hoare em 1962. Dado um vetor, escolhe-se um elemento para particionar o vetor, e os outros elementos são subdivididos em dois subconjuntos: i) aqueles menores que o elemento de partição; ii) aqueles maiores que o elemento de partição Aplica-se o mesmo processo, recursivamente, aos dois conjuntos. Quando um subconjunto tiver menos de dois elementos não precisará mais ser ordenado, encerrando-se o procedimento recursivo. Ver prog42-chap04-pg74.c. A biblioteca padrão inclui uma versão do quicksort que pode ordenar objetos de qualquer tipo. Ver ex13-chap04-new.c

62 Algoritmo quicksort pivot

63 C preprocessor Fornece certas facilidades que conceitualmente são equivalentes a um primeiro passo na compilação. #include – para incluir o conteúdo de um arquivo durante a compilação. #define – para substituir um código por uma seqüência arbitrária de caracteres. Compilação condicional Macros com argumentos

64 Inclusão de arquivos (1)
Facilita a manipulação de grupos de #define(s) e declarações (entre outras coisas). Qualquer linha #include “arquivo” ou #include<arquivo> é substituída pelo conteúdo do arquivo. Se o arquivo estiver entre aspas, a procura pelo arquivo normalmente começa onde o programa fonte se encontra. Se não for achado lá, ou se estiver delimitado por < e >, a busca segue uma regra definida pela implementação.

65 Inclusão de arquivos (2)
Normalmente há diversas linhas #include no início de um arquivo fonte. Servem para incluir comandos #define comuns e declarações extern ou para se ter acesso a declarações de protótipos de funções para as funções da biblioteca de headers como <stdio.h>. #include é o modo preferido de se juntar declarações para um programa grande. Garante que todos os arquivos fonte serão supridos com as mesmas definições e declarações de variáveis, evitando erros indesejáveis.

66 Substituição de macros (1)
#define name replacement-text especifica uma substituição de macro do tipo mais simples, substituindo as ocorrências posteriores do token name por replacement-text. #define TRUE 1 O name em um #define tem a mesma forma que o nome de uma variável; replacement-text é arbitrário. Normalmente o texto substituto é o resto da linha.

67 Substituição de macros (2)
Uma definição longa pode ser continuada em várias linhas, colocando-se um “\” ao final de cada linha a ser continuada. O escopo de um nome definido com #define vai do seu ponto de definição até o fim do arquivo fonte sendo compilado. Uma definição pode usar definições anteriores. As substituições são efetuadas só em códigos e não dentro de cadeias entre aspas.

68 Substituição de macros (3)
Qualquer nome pode ser definido como qualquer texto substituto. #define forever for (;;) /* infinite loop */ define uma nova palavra – forever – para um loop infinito. É possível definir macros com argumentos. #define max(A,B) ((A) > (B) ? (A) : (B)) O uso de max torna-se um código em linha. Cada ocorrência de um parâmetro formal será substituída pelo argumento real correspondente. x = max(p+q, r+s); será substituído por x = ((p+q) > (r+s) ? (p+q) : (r+s));

69 Substituição de macros (4)
As macros servem para diferentes tipos de dados. A expansão de macros em que há efeitos colaterais pode redundar em erros. max(i++, j++) /* WRONG * irá incrementar o maior valor duas vezes. Cuidado com os parênteses, para assegurar a preservação da ordem de avaliação das expressões. #define square(x) x * x /* WRONG */ square (z+1) → z+1*z+1 → 2z+1

70 Substituição de macros (5)
Em <stdio.h>, getchar e putchar são definidas como macros, para se evitar o overhead de invocação de uma função, em tempo de execução, por caracter processado. Nomes podem ser indefinidos com #undef, para assegurar que se trata realmente de uma função, e não de uma macro. #undef getchar int getchar(void) { ... } Parâmetros formais não são substituídos dentro de strings.

71 Substituição de macros (6)
Se, todavia, um nome de parâmetro for precedido por um # no texto substituto, a combinação será expandida em um string entre aspas com o parâmetro substituído pelo argumento real. Ex.: macro para depurar impressão #define dprint(expr) printf(#expr " = %g\n", expr) dprint(x/y) será expandida para: printf("x/y" " = %g\n", x/y); e após a concatenação printf("x/y = %g\n", x/y);

72 Substituição de macros (7)
O operador de pré-processador ## fornece um modo de concatenar argumentos reais durante a expansão de macros. Se um parâmetro no texto substituto for adjacente a ##, o parâmetro é substituído pelo argumento real, o ## e espaços em branco ao redor são removidos e o resultado é reanalisado. #define paste(front, back) front ## back Assim, paste(name, 1) cria o token name1.

73 Inclusão condicional (1)
O pré-processamento pode ser controlado por comandos condicionais. O código é, então, incluído seletivamente, dependendo do valor das condições avaliadas durante a compilação. A linha #if avalia uma expressão inteira constante (excluindo sizeof, typecasts (moldes) ou constantes enum). Se a expressão for != 0, as linhas que se seguem até um #endif ou #elif ou #else são incluídas.

74 Inclusão condicional (2)
O comando do pré-processador #elif é similar ao else- if. A expressão defined (name) em um #if é 1, se name tiver sido definido, e 0, em caso contrário. Exemplo: para se ter certeza que o conteúdo do arquivo hdr.h foi incluído apenas uma vez, o conteúdo do arquivo é envolto por uma condicional. #if !defined(HDR) #define HDR /* contents of hdr.h go here */ #endif

75 Inclusão condicional (3)
A primeira inclusão de hdr.h define o nome HDR; inclusões subseqüentes iráo encontrar o nome definido e saltarão para o #endif. Um estilo similar pode ser usado para se evitar a inclusão de arquivos múltiplas vezes. Se este estilo for usado com consistência, então cada header poderá incluir quaisquer outros headers dos quais dependa, sem que o usuário do header tenha que lidar com a interdependência.

76 Inclusão condicional (4)
Esta seqüência testa o nome SYSTEM, para decidir qual versão de header deverá ser incluída.: #if SYSTEM == SYSV #define HDR "sysv.h" #elif SYSTEM == BSD #define HDR "bsd.h" #elif SYSTEM == MSDOS #define HDR "msdos.h" #else #define HDR "default.h" #endif #include HDR

77 Inclusão condicional (5)
As linhas #ifdef e #ifndef são formas especializadas que testam se um nome está definido. O primeiro exemplo de #if poderia ter sido escrito como: #ifndef HDR #define HDR /* contents of hdr.h go here */ #endif


Carregar ppt "Laboratório de Programação"

Apresentações semelhantes


Anúncios Google