Mudanças entre as edições de "AULA 23 - Programação 1 - Graduação"
(3 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
Linha 122: | Linha 122: | ||
É uma boa prática proteger os arquivos ''headers'' com uma guarda de forma a evitar problemas posteriores. | É uma boa prática proteger os arquivos ''headers'' com uma guarda de forma a evitar problemas posteriores. | ||
− | + | ==Uso do make== | |
O utilitário ''make'' é muito usado para gerenciar a compilação de múltiplos arquivos. #endifamos ver o seu uso através de um exemplo. | O utilitário ''make'' é muito usado para gerenciar a compilação de múltiplos arquivos. #endifamos ver o seu uso através de um exemplo. | ||
Linha 160: | Linha 160: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Arquivo '' | + | Arquivo ''parte2.h'': |
<syntaxhighlight lang=c> | <syntaxhighlight lang=c> | ||
Linha 211: | Linha 211: | ||
O make interpreta o Makefile do diretório corrente tenta construir o arquivo programa_bin usando a primeira regra do arquivo. Se este estiver atualizado nada será feito. Caso ele observe que main.o ou parte2.o estão desatualizados, ele tenta remontá-los usando as respectivas regras. Note que ao lado do nome da regra estão as dependências. | O make interpreta o Makefile do diretório corrente tenta construir o arquivo programa_bin usando a primeira regra do arquivo. Se este estiver atualizado nada será feito. Caso ele observe que main.o ou parte2.o estão desatualizados, ele tenta remontá-los usando as respectivas regras. Note que ao lado do nome da regra estão as dependências. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | {| border="1" cellpadding="5" cellspacing="0" | ||
+ | ! style="background: #cdc5bf;" | [[AULA 22 - Programação 1 - Graduação | << ]] | ||
+ | ! style="background: #cdc5bf;" | Aula 23 | ||
+ | ! style="background: #cdc5bf;" | [[AULA 24 - Programação 1 - Graduação | >> ]] | ||
+ | |} |
Edição atual tal como às 16h44min de 11 de junho de 2015
Separação do Programa em Múltiplos Arquivos
Muitas vezes o programa se torna grande demais e o uso de múltiplos arquivos fonte torna-se necessário. A divisão também permite o desenvolvimento organizado do projeto, onde cada arquivo contém um grupo de instruções e variáveis globais relacionados com uma determinada parte do sistema (subsistema ou módulo).
É necessário, no entanto, criar arquivos cabeçalho (headers ou .h) para declarar protótipos de funções e variáveis globais cuja visibilidade deve ser exportada para outros arquivos. Exemplo:
Seja um projeto com dois arquivos. No arquivo t1.c existe o seguinte conteúdo:
#include <stdio.h>
#include "t2.h"
int y;
void alfa(int x)
{
printf("x=%d\n",x);
}
main()
{
y = 20;
alfa(2);
beta();
}
Observe que a função main() usa as funções a alfa() e beta(). Mas beta() não está implementada em t1.c. Ela está implementada em um outro arquivo t2.c. Neste caso, para que o compilador possa validar os parâmetros e o retorno da função beta() é necessário incluir um arquivo header t2.h que possui tais informações.
Arquivo t2.h:
extern void beta();
Note o uso da palavra extern para informação do PROTÓTIPO da função.
O arquivo t2.c possui a implementação de beta():
#include <stdio.h>
#include "t1.h"
void beta()
{
alfa(23);
printf ("y=%d\n",y);
}
Note que beta() usa a função alfa() que está implementada em t1.c. Neste caso ela inclui o arquivo t1.h que contém o protótipo de alfa():
extern int y;
extern void alfa(int x); /* extern aqui é opcional */
Regras Gerais para Construção do Header
- Se você definir uma variável global em arquivo fonte, digamos no t1.c, e deseja que outros arquivos "vejam" está variável então coloque uma declaração desta variável em um arquivo header (t1.h) com a palavra chave extern.
NOTA: note que existe uma diferença entre definir e declarar. Se você cria uma variável global, por exemplo, int x; no arquivo fonte t1.c, você está definindo a variável. Será alocada uma área de memória para esta veriável. Se você declara a variável no t1.h usando o extern, você simplesmente está informando que esta variável existe e qual tipo possui.
- Nunca defina a variável no header pois estará abrindo a possibilidade para que cada arquivo que inclua este header crie uma instância desta variável;
- Se você quer publicar (informar) outros arquivos fonte sobre funçṍes de um arquivo, por exemplo, a função beta() do exemplo passado, então insira uma declaração da função no header. Neste caso a palavra chave é opcional.
Ver discussão em: [1]
O problema de múltiplas inclusão de headers [2]
Seja um arquivo avo.h:
struct familia {
};
E um arquivo pai,h:
#include "avo.h"
Finalmente um arquivo filho.h:
#include "avo.h"
#include "pai.h"
Considere o fonte filho.c
#include "filho.h"
Se este arquivo for compilado, teremos um erro porque devido a dupla inclusão do header avo.h a estrutura familia estará sendo duplicada.
Compilação Condicional e Guard Headers
As diretivas de pré-compilação #ifndef e #ifdef são usadas quando queremos compilar "condicionalmente" determinado bloco de código.
Por exemplo, podemos criar guard headers usando a diretiva #ifndef de forma a inserir unicamente um código de um header. No exemplo anterior, o arquivo avo.c poderia ser "guardado" da forma:
#ifndef AVO_H
#define AVO_H
struct familia {
};
#endif
Neste caso, guando o arquivo filho.c for compilado, o arquivo avo.h é duplamente incluído. O compilador, ao encontrar a diretiva #ifndef AVO_H da primeira inclusão, observa que o símbolo AVO_H não foi definido ainda (com um #define). Neste caso, ele compila o código que se segue. No código que se segue o símbolo AVO_H é definido. Ao encontrar a segunda inclusão de de avo.h, o compilador novamente se depara com a diretiva #ifndef AVO_H. Neste momento, o símbolo AVO_H já foi definido e o compilador ignora o código que se segue até encontrar o #endif. É a compilação condicional...
É uma boa prática proteger os arquivos headers com uma guarda de forma a evitar problemas posteriores.
Uso do make
O utilitário make é muito usado para gerenciar a compilação de múltiplos arquivos. #endifamos ver o seu uso através de um exemplo.
Seja um programa executável programa_bin formado a partir de dois arquivos fontes: main.c e parte2.c
Seja main.h um header de main.c e parte2.h um header de parte2.c. Os arquivos são:
Arquivo main.h:
#ifndef MAIN_H
#define MAIN_H
extern void alfa(char *p);
#endif
Arquivo main.c:
#include <stdio.h>
#include "parte2.h"
#include "main.h"
void alfa(char *p)
{
printf("ALFA: %s\n", p);
}
main()
{
alfa ("Alo Mundo - Invocado de main");
beta ("Alo Mundo - Invocado de main");
}
Arquivo parte2.h:
#ifndef PARTE2_H
#define PARTE2_H
extern void beta(char *p);
#endif
Arquivo parte2.c:
#include <stdio.h>
#include "parte2.h"
#include "main.h"
void beta(char *p)
{
printf("BETA: %s\n", p);
alfa ("Alo Mundo - Invocado de BETA");
}
Seja o arquivo Makefile:
#arquivo Makefile
programa_bin: main.o parte2.o
gcc main.o parte2.o -o programa_bin
main.o: main.c main.h parte2.h
gcc -c main.c
parte2.o: parte2.c parte2.h main.h
gcc -c parte2.c
clean:
rm *.o
rm -r ./install
install:
mkdir install
mv programa_bin ./install
Para chamar o make basta fazer:
make
O make interpreta o Makefile do diretório corrente tenta construir o arquivo programa_bin usando a primeira regra do arquivo. Se este estiver atualizado nada será feito. Caso ele observe que main.o ou parte2.o estão desatualizados, ele tenta remontá-los usando as respectivas regras. Note que ao lado do nome da regra estão as dependências.
<< | Aula 23 | >> |
---|