Estruturação do Programa Queremos organizar um programa de forma a: dividi-lo em partes que realizem tarefas específicas, resumindo um problema complexo a vários problemas mais simples simplifica a elaboração e manutenção do código erros são mais facilmente detetados permite escrever funções genéricas que possam ser usadas por vários programas facilita o trabalho em equipe, com diferentes grupos trabalhando em cada módulo independente. permite separar o uso da implementação (encapsulamento) 1
Funções Já utilizamos várias funções da biblioteca padrão: scanf, printf, cos, sqrt Até agora só construímos uma função - a main - todo programa precisa de uma e é por ela que começa a execução do programa. A sintaxe de uma função é: tipo nome (tipo1 var1, tipo2 var2,...) { linhas de código return valor; Mostrar o programa fatorial.c 2
Ao chegar na chamada da função, a execução de main é interrompida. O valor de n é passado para a função fatorial, que executa suas linhas de comando até encontar um return Ao encontrar um return, a execução da função é interrompida e o valor de fat é retornado e atribuído à variável nfatorial Observe que pode haver mais de um return As funções podem aparecer em qualquer ordem dentro de um arquivo, mesmo antes de main, ou em arquivos separados. Mas não pode ser definida dentro de outra função Uma função, como qualquer identificador, só pode ser utilizada depois de ser declarada 3
Protótipo Para toda função que criamos, devemos fazer uma declaração, mostrando o protótipo da função, da seguinte forma: tipo nome (tipo1 var1, tipo2 var2,...); Normalmente as declarações ficam no começo do arquivo, antes de main Os protótipos são necessários para o compilador conferir se o uso da função está correto. Ex: double sin( double x ) double x, y; y = sin(x); O nome das variáveis podem ser omitidos na declaração (não na definição da função). Mas isso não é uma boa prática. tipo nome (tipo1, tipo2,...); 4
Tipos de Funções Funções em C podem retornar qualquer tipo de variáveis, menos arrays ou funções. A expressão que aparece em return valor deve ser do tipo da função. Podem também não retornar nada. Neste caso o tipo é void void imprime(int n); int main(){ int n; printf("entre com o valor de n\n"); scanf ("%d", &n); imprime (n); return 0; void imprime(int n){ printf("o valor de n eh:%d\n", n); 5
Lista de Argumentos Os nomes na lista da definição são independentes dos nomes na chamada da função. int soma(int i, int j, int k); int main(){ int l, m, n, s; printf("entre com os valores a serem somados\n"); scanf ("%d %d %d", &l, &m, &n); s = soma(l, m, n); printf("a soma eh %d",s); return 0; int soma(int o, int p, int q){ return o+p+q; i, j, k ou o, p, q: argumentos formais l, m, n: argumentos reais 6
Passagem por valor Em C, não é a variável que é passada para a função, mas sim o seu valor. #include <stdio.h> double fatorial (int n); int main{ double fatorial; int n=5; nfatorial = fatorial(n); printf ("O fatorial de %d eh %lf\n",n, nfatorial); return 0; double fatorial (int n){ double fat = 1.0; for ( ; n > 1; n--) fat *= n; return fat; O valor de n foi alterado na função fatorial, mas não foi alterado em main. 7
Passagem de Array é diferente Fazer uma cópia de um array seria muito custoso. O que passamos é o endereço do início do vetor e devemos passar também a dimensão do vetor. void inicio (double a[], int n); int main{ double r[3]; int i; inicio(r, 3); for (i = 0; i < 3; i++) printf("r[%d] = %lf \n", i, r[i]); return 0; void inicio (double a[], int n){ int i; for (i = 0; i < n; i++) a[i] = 0.0; Neste caso, se alterarmos o array na função, estamos alterando o original 8
Variáveis Automáticas e Estáticas Variáveis declaradas dentro de uma função são criadas e inicializadas a cada vez que a função é chamada Elas são armazenadas temporariamente em uma região de memória e deixam de existir ao término da execução da função. Essas variáveis são chamadas de automáticas. Se quisermos preservar o valor de variáveis dentro de uma função devemos declará-las como estáticas A criação de variáveis estáticas é feita em tempo de compilação e deve-se inicializá-la na hora da declaração Quando a função é executada, a variável estática tem o último valor que lhe foi atribuído na última passagem pela função 9
void contar(int i); int main{ const int n = 10; int i; for (i=0; i < n; i++) contar(i); return 0; void contar(int i){ static int contador = 1; int k = 0; k++; printf( "contador = %d contador++; k = %d\n", contador, k); 10
Escopo Escopo das variáveis: em que região do código uma variável é acessível. Quando uma variável é criada dentro de uma função ela só é vista em seu interior. São variáveis locais As automáticas até deixam de existir ao término da função As estáticas não deixam de existir, mas também só são vistas dentro da função. Mas podemos declarar variáveis fora das funções, antes de main ou entre as declarações das funções. São chamadas de variáveis globais São acessíveis do ponto de declaração até o fim do arquivo São estáticas: permanecem na memória durante toda a execução do programa 11
São vistas por todas as funções definidas fisicamente abaixo delas. É uma maneira prática de fazer com que funções diferentes compartilhem a mesma variável. Porém deve ser usada com moderação, pois uma função pode alterar o valor de uma variável global por engano. 12
#include <stdio.h> int soma (int i); int j = 5; int main(){ int i = 1; printf ("j = %d\n", j); printf ("k = %d\n", k); // Erro: k ainda nao declarada printf ("l = %d\n", l); // Erro: l e local em soma() printf ("i+j+k+l = %d\n", soma(i)); return 0; int k = 10; int soma(int i){ int l = 4; return i+j+k+l; 13
$ gcc -Wall -o escopo escopo.c escopo.c: In function int main() : escopo.c:10: error: k undeclared (first use this function) escopo.c:10: error: (Each undeclared identifier is reported only once for function it appears in.) escopo.c:11: error: l undeclared (first use this function) escopo.c: At global scope: escopo.c:16: error: int k used prior to declaration 14
Constantes Simbólicas Frequentemente usamos o mesmo valor em diversas partes do programa. É conveniente usarmos as constantes simbólicas Ex.: #define VSOM 340.0 Sempre que VSOM for encontrado, ele será substituído por 340.0 pelo pré-processador #define NDIM 3 void inicio (double a[], int n); int main{ double r[ndim]; int i; inicio(r, NDIM); for (i = 0; i < NDIM; i++) printf("r[%d] = %lf \n", i, r[i]); return 0; 15
Macros Funções permitem que tarefas sejam separadas em regiões bem determinadas, facilitando a organização e a verificação do código. Mas: as informações locais devem ser guardadas no ponto de chamada da função e novas variáveis devem ser criadas e inicializadas, consumindo tempo de execução Se a função puder ser escrita em uma só linha podemos usar MACROS #define SQR(x) ((x)*(x)) x pode ser um número, uma variável ou uma expressão mais complexa. Sempre que SQR(x) for encontrado, ele será substituído por ((x)*(x)) pelo pré-processador 16
#include <stdio.h> #define SQR(x) ((x)*(x)) #define CUBE(x) ((x)*(x)*(x)) #define MAXIMO(x,y) (((x) > (y))? (x) : (y)) #define MINIMO(x,y) (((x) < (y))? (x) : (y)) #define SOMA(a,b) a+b // CUIDADO: Definicao inadequada int main(){ double x = -5.0, y = 10.0; printf("sqr(x) = %f\n", SQR(x)); printf("cube(x) = %f\n", CUBE(x)); printf("maximo(x,y) = %f\n", MAXIMO(x,y)); printf("minimo(x,y) = %f\n", MINIMO(x,y)); printf("dobro de x+y = %f\n", 2*SOMA(x,y));// Resposta errada return 0; SQR(x) = 25.0 CUBE(x) = -125.0 MAXIMO(x,y) = 10.0 MINIMO(x,y) = -5.0 2*SOMA(x+y) = 0.0 (devia dar 10.0!) 17
Divisão em arquivos Um programa pode ser dividido em vários arquivos. Arquivos de cabeçalho (headers): contém declarações de funções e de variáveis globais, constantes simbólicas, macros,... Arquivos de implementação: Contém a implementação de uma ou mais funções. Podem ser adicionados na hora da compilação ou podem ser pré-compilados separadamente ( arquivo objeto) e juntado aos outros na hora da compilação (linker) O programa principal, que contém a função main, entre outras. 18
Headers Um programa grande, com vários includes, protótipos de funções, macros e constantes simbólicas, torna-se rapidamente sujo, de difícil leitura, devido à grande quantidade de declarações. Os headers são úteis para organizar e esconder tudo isso. podemos criar um arquivo chamado my math.h com o seguinte conteúdo: #include <math.h> #define SQR(x) #define CUBE(x) #define MAXIMO(x,y) #define MINIMO(x,y) ((x)*(x)) ((x)*(x)*(x)) (((x) > (y))? (x) : (y)) (((x) < (y))? (x) : (y)) E para utilizar esse header no programa.c incluímos: #include "my math.h" 19
Exemplo de divisão em arquivos Vejamos com separar o programa abaixo em vários arquivos #include <stdio.h> double fatorial (int n); int main{ double fatorial; int n=5; nfatorial = fatorial(n); printf ("O fatorial de %d eh %lf\n",n, nfatorial); return 0; double fatorial (int n){ double fat = 1.0; for ( ; n > 1; n--) fat *= n; return fat; 20
Arquivo fatorial.h: double fatorial (int n); Deve ser incluído pelo pré-processador com #include "fatorial.h" Arquivo fatorial.c: double fatorial (int n){ double fat = 1.0; for ( ; n > 1; n--) fat *= n; return fat; Pode ser pré-compilado com o comando gcc -c fatorial.c O arquivo objeto fatorial.o é criado. É um arquivo binário, mas não é executável 21
Arquivo com o programa principal principal.c: #include <stdio.h> #include "fatorial.h" int main{ double fatorial; int n=5; nfatorial = fatorial(n); printf ("O fatorial de %d eh %lf\n",n, nfatorial); return 0; Podemos criar um programa executável completo com o comando gcc -o principal principal.c fatorial.o O pré-processador inclui os arquivos stdio.h e fatorial.h O compilador compila o arquivo principal.c O linker adiciona o arquivo fatorial.o criando o executável principal 22
A separação do código em diferentes arquivos faz com que não seja necessário copiar cada função para dentro do seu programa Essa função não precisa ser recompilada cada vez que for usada Bibliotecas: Várias funções pré-compiladas podem ser agrupadas em um único arquivo binário: Uma biblioteca Uma biblioteca pode ser adicionada a um programa com a opção -l do compilador seguido de um nome que indica a biblioteca. O comando gcc -o calculo calculo.c -lm cria o executável calculo incluindo a biblioteca de funções matemáticas do sistema. (As declarações das funções estão no arquivo de cabeçalho correspondente) 23