Estruturac¸˜ao do Programa
Queremos organizar um programa de forma a:
dividi-lo em partes que realizem tarefas espec´ıficas, resumindo um problema complexo a v´arios problemas mais simples
❒ simplifica a elaborac¸˜ao e manutenc¸˜ao do c´odigo ❒ erros s˜ao mais facilmente detetados
❒ permite escrever func¸ ˜oes gen´ericas que possam ser usadas por v´arios
programas
❒ facilita o trabalho em equipe, com diferentes grupos trabalhando em
cada m´odulo independente.
Func¸˜oes
J´a utilizamos v´arias func¸ ˜oes da biblioteca padr˜ao: scanf, printf, cos, sqrt
At´e agora s´o constru´ımos uma func¸˜ao - a main - todo programa precisa de
uma e ´e por ela que comec¸a a execuc¸˜ao do programa. A sintaxe de uma func¸˜ao ´e:
tipo nome (tipo1 var1, tipo2 var2,...) {
linhas de c´odigo return valor;
}
❒ Ao chegar na chamada da func¸˜ao, a execuc¸˜ao de main ´e interrompida.
❒ O valor de n ´e passado para a func¸˜ao fatorial, que executa suas linhas de comando at´e encontar um return
❒ Ao encontrar um return, a execuc¸˜ao da func¸˜ao ´e interrompida e o valor de fat ´e retornado e atribu´ıdo `a vari´avel nfatorial
❒ Observe que pode haver mais de um return
❒ As func¸˜oes podem aparecer em qualquer ordem dentro de um arquivo, mesmo antes de main, ou em arquivos separados. Mas n˜ao pode ser definida dentro de outra func¸˜ao
❒ Uma func¸˜ao, como qualquer identificador, s ´o pode ser utilizada depois de ser declarada
Prot´
otipo
❒ Para toda func¸˜ao que criamos, devemos fazer uma declarac¸˜ao, mostrando o prot´otipo da func¸ ˜ao, da seguinte forma:
tipo nome (tipo1 var1, tipo2 var2,...);
❒ Normalmente as declarac¸ ˜oes ficam no comec¸o do arquivo, antes de
main
❒ Os prot´otipos s˜ao necess´arios para o compilador conferir se o uso da func¸˜ao est´a correto. Ex: double sin( double x )
double x, y; y = sin(x);
❒ O nome das vari´aveis podem ser omitidos na declarac¸˜ao (n˜ao na definic¸˜ao da func¸˜ao). Mas isso n˜ao ´e uma boa pr´atica.
Tipos de Func¸ ˜oes
❒ Func¸˜oes em C podem retornar qualquer tipo de vari´aveis, menos arrays ou func¸ ˜oes.
❒ A express˜ao que aparece em return valor deve ser do tipo da func¸˜ao.
❒ Podem tamb´em n˜ao retornar nada. Neste caso o tipo ´e 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); }
Lista de Argumentos
❒ Os nomes na lista da definic¸˜ao s˜ao independentes dos nomes na
chamada da func¸˜ao.
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
Passagem por valor
Em C, n˜ao ´e a vari´avel que ´e passada para a func¸˜ao, 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;
}
Passagem de Array ´
e diferente
Fazer uma c´opia de um array seria muito custoso. O que passamos ´e o
enderec¸o do in´ıcio do vetor e devemos passar tamb´em a dimens˜ao 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; }
Vari´
aveis Autom´
aticas e Est´
aticas
❒ Vari´aveis declaradas dentro de uma func¸˜ao s˜ao criadas e inicializadas a cada vez que a func¸˜ao ´e chamada
❒ Elas s˜ao armazenadas temporariamente em uma regi˜ao de mem´oria e
deixam de existir ao t´ermino da execuc¸˜ao da func¸˜ao.
❒ Essas vari´aveis s˜ao chamadas de autom´aticas.
❒ Se quisermos preservar o valor de vari´aveis dentro de uma func¸˜ao
devemos declar´a-las como est´aticas
❒ A criac¸˜ao de vari´aveis est´aticas ´e feita em tempo de compilac¸˜ao e deve-se inicializ´a-la na hora da declarac¸˜ao
❒ Quando a func¸˜ao ´e executada, a vari´avel est´atica tem o ´ultimo valor
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 k = %d\n", contador, k); contador++;
Escopo
❒ Escopo das vari´aveis: em que regi˜ao do c´odigo uma vari´avel ´e
acess´ıvel.
❒ Quando uma vari´avel ´e criada dentro de uma func¸˜ao ela s´o ´e vista em
seu interior. S˜ao vari´aveis locais
❒ As autom´aticas at´e deixam de existir ao t´ermino da func¸˜ao
❒ As est´aticas n˜ao deixam de existir, mas tamb´em s´o s˜ao vistas dentro da func¸˜ao.
❒ Mas podemos declarar vari´aveis fora das func¸ ˜oes, antes de main ou entre as declarac¸ ˜oes das func¸ ˜oes. S˜ao chamadas de vari´aveis globais
❒ S˜ao acess´ıveis do ponto de declarac¸˜ao at´e o fim do arquivo
❒ S˜ao est´aticas: permanecem na mem´oria durante toda a execuc¸˜ao do programa
❒ S˜ao vistas por todas as func¸ ˜oes definidas fisicamente abaixo delas.
❒ E uma maneira pr´atica de fazer com que func¸ ˜oes diferentes´ compartilhem a mesma vari´avel.
❒ Por´em deve ser usada com moderac¸˜ao, pois uma func¸˜ao pode alterar o valor de uma vari´avel global por engano.
#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; }
$ 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 each function it appears in.)
escopo.c:11: error: ‘l’ undeclared (first use this function) escopo.c: At global scope:
Constantes Simb ´
olicas
❒ Frequentemente usamos o mesmo valor em diversas partes do programa. ´E conveniente usarmos as constantes simb ´olicas Ex.:
#define VSOM 340.0
❒ Sempre que VSOM for encontrado, ele ser´a substitu´ıdo por 340.0 pelo
pr´e-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;
Macros
❒ Func¸˜oes permitem que tarefas sejam separadas em regi ˜oes bem determinadas, facilitando a organizac¸˜ao e a verificac¸˜ao do c´odigo.
❒ Mas:
❍ as informac¸ ˜oes locais devem ser guardadas no ponto de chamada
da func¸˜ao
❍ e novas vari´aveis devem ser criadas e inicializadas, consumindo tempo de execuc¸˜ao
❒ Se a func¸˜ao puder ser escrita em uma s´o linha podemos usar MACROS
❒ #define SQR(x) ((x)*(x))
❒ x pode ser um n´umero, uma vari´avel ou uma express˜ao mais complexa.
❒ Sempre que SQR(x) for encontrado, ele ser´a substitu´ıdo por
#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
Divis˜ao em arquivos
Um programa pode ser dividido em v´arios arquivos.
❒ Arquivos de cabec¸alho (headers):
cont´em declarac¸ ˜oes de func¸ ˜oes e de vari´aveis globais, constantes simb´olicas, macros, ...
❒ Arquivos de implementac¸˜ao:
Cont´em a implementac¸˜ao de uma ou mais func¸ ˜oes. Podem ser adicionados na hora da compilac¸˜ao ou
podem ser pr´e-compilados separadamente ( arquivo objeto) e juntado aos outros na hora da compilac¸˜ao (linker)
Headers
❒ Um programa grande, com v´arios includes, prot´otipos de func¸ ˜oes, macros e constantes simb´olicas, torna-se rapidamente “sujo”, de dif´ıcil leitura, devido `a grande quantidade de declarac¸ ˜oes. Os headers s˜ao
´uteis para organizar e esconder tudo isso.
❒ podemos criar um arquivo chamado my math.h com o seguinte
conte´udo:
#include <math.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))
❒ E para utilizar esse header no programa .c inclu´ımos:
Exemplo de divis ˜ao em arquivos
Vejamos com separar o programa abaixo em v´arios 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;
❒ Arquivo fatorial.h:
double fatorial (int n);
Deve ser inclu´ıdo pelo pr´e-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´e-compilado com o comando
gcc -c fatorial.c
O arquivo objeto fatorial.o ´e criado. ´
❒ 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´avel completo com o comando
gcc -o principal principal.c fatorial.o
O pr´e-processador inclui os arquivos stdio.h e fatorial.h
O compilador compila o arquivo principal.c
O linker adiciona o arquivo fatorial.o
A separac¸˜ao do c´odigo em diferentes arquivos
❍ faz com que n˜ao seja necess´ario copiar cada func¸˜ao para dentro do seu programa
❍ Essa func¸˜ao n˜ao precisa ser recompilada cada vez que for usada
Bibliotecas:
V´arias func¸ ˜oes pr´e-compiladas podem ser agrupadas em um ´unico arquivo bin´ario: Uma biblioteca
Uma biblioteca pode ser adicionada a um programa com a opc¸˜ao -l
do compilador seguido de um nome que indica a biblioteca. O comando
gcc -o calculo calculo.c -lm
cria o execut´avel calculo incluindo a biblioteca de func¸ ˜oes matem´aticas do sistema.