Mudanças entre as edições de "AULA 25 - Programação 1 - Engenharia"
Linha 129: | Linha 129: | ||
=REGRAS GERAIS PARA CONSTRUÇÃO DO HEADER= | =REGRAS GERAIS PARA CONSTRUÇÃO DO HEADER= | ||
− | + | <ol> | |
+ | <li> | ||
+ | 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 | 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 | 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. | a variável no t1.h usando o extern, você simplesmente está informando que esta variável existe e qual tipo possui. | ||
− | + | </li> | |
− | + | <li> | |
+ | 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; | ||
+ | </li> | ||
+ | <li> | ||
+ | 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. | ||
+ | </li> | ||
+ | </ol> | ||
Ver discussão em: | Ver discussão em: |
Edição das 09h56min de 5 de novembro de 2018
Conceito de PROTÓTIPO de função
Note que a ordem de construção de funções impacta na compilação de um programa:
Tente compilar:
main()
{
int a=5,b=c;
swap_var(&a,&b)
}
void swap_var(int *p1, int *p2)
{
int x;
x=*p1;
*p1=*p2;
*p2=x;
}
E agora tente compilar:
void swap_var(int *p1, int *p2)
{
int x;
x=*p1;
*p1=*p2;
*p2=x;
}
main()
{
int a=5,b=c;
swap_var(&a,&b)
}
Então sempre devemos declarar tudo antes do main() ???
Com certeza não, até porque este probblema pode aparecer em outras funções que não sejam o main...
Devemos usar PROTÓTIPO de funções:
/* protótipo de ''swap_var'' */
void swap_var(int *p1, int *p2);
main()
{
int a=5,b=c;
swap_var(&a,&b)
}
void swap_var(int *p1, int *p2)
{
int x;
x=*p1;
*p1=*p2;
*p2=x;
}
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. É OPCIONAL para funções.
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. Ela também faz uso da variável global y definida 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. Vamos 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 parte1.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.
Mais detalhes do make ver em https://www.gnu.org/software/make/manual/html_node/Simple-Makefile.html#Simple-Makefile