Projeto em arquivos separados

Software e Hardware para uC PIC

Moderadores: andre_luis, 51, guest2003, Renie

Projeto em arquivos separados

Mensagempor ÁgioFelipe » 10 Jan 2013 08:58

Olá amigos, estou escrevendo um projeto no C18 e resolvi organizar de forma que o programa fique em arquivos separados. Por enquanto estou com dois arquivos:
- Principal.c, como o nome já diz é programa propriamente dito
- Funções.c, irá guardar todas as funções do programa principal

1ª pergunta: Vi que mesmo armazenando todas as funções no Funções.c, ainda precisava fazer a declaração de cada função no Principal.c caso contrário aparece aquele warning de função sem protótipo.

2ª: A compilação só acontece sé colocar #include <p18F4550.h> nos dois arquivos, caso contrário dá erro. É assim mesmo, tenho q colocar esse include nos 2?

Valeu.
ÁgioFelipe
Word
 
Mensagens: 626
Registrado em: 27 Out 2006 20:04

Mensagempor RobL » 10 Jan 2013 16:58

Sim e sempre que alguma coisa do seu arquivo .c depender do que esta no arquivo .h ou (de forma deselegante ou não) o que estiver em outro .c. Neste caso deve incluir o .c no arquivo que depende dele.
São as conhecidas e problemáticas dependências.
Usando seu caso como exemplo:
Tem algo nos dois arquivos .c que dependem de Picxxxx.h. Então vai ser incluído nos dois arquivos .c.
RobL
Dword
 
Mensagens: 1546
Registrado em: 20 Fev 2007 17:56

Mensagempor andre_luis » 10 Jan 2013 18:46

RobL,


Não sei se isso varia de um compilador para outro, mas já utilizei literalmente o mesmo programa no AVOCET e no KEIL e não houve a necessidade de declarar o protótipo na função principal, mas bastou incluir o .H do arquivo, onde estavam contidas esses protótipos.


+++
"Por maior que seja o buraco em que você se encontra, relaxe, porque ainda não há terra em cima."
Avatar do usuário
andre_luis
Dword
 
Mensagens: 5447
Registrado em: 11 Out 2006 18:27
Localização: Brasil - RJ

Mensagempor RobL » 10 Jan 2013 20:53

André, procurei responder, de forma Ágil, sem Ágio, a segunda pergunta do Felipe, visto que a primeira pergunta é uma afirmação. Neste caso disse que sim, deve informar (#include) o arquivo .h no .c.

Já que você tocou numa outra questão, a de haver ou não necessidade de declarar um protótipo no próprio arquivo .c, curiosamente no GCC e derivados (WINAVR dentre outros), quando a função é qualificada como estática (static), ele não aceitará a declaração em um outro arquivo, mesmo sendo .h e incluindo no .c, no qual a função é chamada.
Ele considera estática somente a função chamada em um mesmo arquivo, por definição (Static é usado para fim de otimização).
No entanto, quando se usa um procedimento não formal, no mesmo GCC ou derivados, incluindo arquivos .c e . h em outro .c, fazendo uma pequena lambança, as vezes "facilitadora", dependendo da posição em que ficou essa função (static), ele não vai reclamar. Porém, não sei dizer se simplesmente a inteligência do processo de compilação, como um todo, ignorou a qualificação ou não. Nunca verifiquei o resultado para comparar.
RobL
Dword
 
Mensagens: 1546
Registrado em: 20 Fev 2007 17:56

Mensagempor barboza » 10 Jan 2013 22:32

RobL escreveu:André, procurei responder, de forma Ágil, sem Ágio, a segunda pergunta do Felipe, visto que a primeira pergunta é uma afirmação. Neste caso disse que sim, deve informar (#include) o arquivo .h no .c.

Já que você tocou numa outra questão, a de haver ou não necessidade de declarar um protótipo no próprio arquivo .c, curiosamente no GCC e derivados (WINAVR dentre outros), quando a função é qualificada como estática (static), ele não aceitará a declaração em um outro arquivo, mesmo sendo .h e incluindo no .c, no qual a função é chamada.
Ele considera estática somente a função chamada em um mesmo arquivo, por definição (Static é usado para fim de otimização).
No entanto, quando se usa um procedimento não formal, no mesmo GCC ou derivados, incluindo arquivos .c e . h em outro .c, fazendo uma pequena lambança, as vezes "facilitadora", dependendo da posição em que ficou essa função (static), ele não vai reclamar. Porém, não sei dizer se simplesmente a inteligência do processo de compilação, como um todo, ignorou a qualificação ou não. Nunca verifiquei o resultado para comparar.


Funções com static não são para otimizações e sim define que a função é visível somente no arquivo na qual foi declarada. Neste caso o protótipo pode ficar no inicio do arquivo .c e não no .h.

Ao invés de fazer o include de arquivo .c dentro de arquivos .c, o certo é fazer no projeto se usado IDE ou no makefile.

Includes só de .h nestes casos.
Os homens mentiriam muito menos se as mulheres fizessem menos perguntas.
Avatar do usuário
barboza
Word
 
Mensagens: 948
Registrado em: 17 Out 2006 13:42
Localização: Longe de onde gostaria de estar

Mensagempor RobL » 11 Jan 2013 09:38

Funções com static não são para otimizações e sim define que a função é visível somente no arquivo na qual foi declarada. Neste caso o protótipo pode ficar no inicio do arquivo .c e não no .h.


O fato de ser static é justamente usado no processo de otimização para transformá-la em inline. Claro que a otimização é compiler dependente e também do tipo setado de otimização.

Seria fácil para qualquer compilador verificar se sua função está em um só arquivo, ainda que não qualificada como static e certamente seria automaticamente qualificada como static e optimizada para inline. Mas a falta deste qualificador, em um enorme programa, aumentaria muito o tempo de compilação.
Quando múltiplas chamadas são feitas em uma função static, ainda que no mesmo arquivo, será optimizada , antecipando o carregamento para registros em um ponto estratégico do programa.
O qualificador static, por definição, informa ao compilador que ele não precisa varrer todos os arquivos a procura desta função e decidir, de forma mais eficiente, onde carregá-la, o que faz parte de sua estratégia de otimização.
Aliás, um bom compilador aproveitará bem toda informação que o programador lhe passar no sentido de auxiliá-lo no processo de optimização.
RobL
Dword
 
Mensagens: 1546
Registrado em: 20 Fev 2007 17:56

Mensagempor barboza » 11 Jan 2013 10:00

RobL escreveu:
Funções com static não são para otimizações e sim define que a função é visível somente no arquivo na qual foi declarada. Neste caso o protótipo pode ficar no inicio do arquivo .c e não no .h.


O fato de ser static é justamente usado no processo de otimização para transformá-la em inline. Claro que a otimização é compiler dependente e também do tipo setado de otimização.

Seria fácil para qualquer compilador verificar se sua função está em um só arquivo, ainda que não qualificada como static e certamente seria automaticamente qualificada como static e optimizada para inline. Mas a falta deste qualificador, em um enorme programa, aumentaria muito o tempo de compilação.
Quando múltiplas chamadas são feitas em uma função static, ainda que no mesmo arquivo, será optimizada , antecipando o carregamento para registros em um ponto estratégico do programa.
O qualificador static, por definição, informa ao compilador que ele não precisa varrer todos os arquivos a procura desta função e decidir, de forma mais eficiente, onde carregá-la, o que faz parte de sua estratégia de otimização.
Aliás, um bom compilador aproveitará bem toda informação que o programador lhe passar no sentido de auxiliá-lo no processo de optimização.



Bem, você estava dizendo sobre otimizações no processo de compilação e não na execução do código em si, como eu havia entendido.

Sobre inline functions. Esta deve ser usada com cautela e só onde realmente precisa. Vai melhorar a performance da execução do código retirando os "push e pop" or call e return, mas se houver múltiplas chamadas a mesma função, você poderá múltiplas instancias da mesma função no seu código, o que aumentaria o tamanho do código.
Os homens mentiriam muito menos se as mulheres fizessem menos perguntas.
Avatar do usuário
barboza
Word
 
Mensagens: 948
Registrado em: 17 Out 2006 13:42
Localização: Longe de onde gostaria de estar

Mensagempor Emilio Eduardo » 12 Jan 2013 17:22

[Atenção: Isso não está completo, possui só metade da informação e ainda vai ser revisado]


Eu sempre achei complicado entender o básico sobre esse processo de separar um programa em arquivos, mas depois que aprendi vi que é simplesmente genial e muito prático.

Acho que a chave de tudo é entender o processo de "Compilação" como um todo, entre aspas por que na verdade Compilação é uma parte do processo inteiro que é disparado quando damos um "build".

São três processos separados:

Pré-Processamento
Compilação
Link-Edição

O resultado de um processo, vai para o outro, formando uma cadeia.

Cada um desses processos tem um papel muito importante para viabilizar o arquivo final.

O Pré-Processamento tem como objetivo interpretar as diretivas referentes a ele, aquelas como #include, #ifdef, #define e algumas outras, assim como os "//" e "/*".

O que ele faz é simplesmente pegar cada um dos arquivos do projeto, ler e fazer as devidas alterações no texto do arquivo. Uma delas é retirar todos os comentários, outra é adicionar o conteúdo(Em texto mesmo) de outro arquivo, como um copiar e colar e diversas outras como interpretar as diretivas de #if e substituir os #defines no texto.

Feito isso, o resultado do pré-processamento de cada um dos arquivos .c e .h vai para o compilador.

O processo de compilação é um tanto complicado, como vocês devem imaginar, mas o importante é que o papel dele é transformar código fonte em código objeto, uma "linguagem" muito mais próxima da linguagem de máquina. Entretanto esse processo pode deixar várias coisas faltando para ser executado.

Isso acontece por que você pode chamar uma função pertencente a um arquivo a partir de outro mas o processo de compilação ocorre arquivo por arquivo, sem juntar os arquivos propriamente ditos. Dessa maneira, quando o compilador encontra o protótipo de uma função que está definida em outro arquivo ele deixa um aviso dentro do arquivo objeto dizendo: "Em algum outro arquivo do projeto deve existir uma função chamada tal, com retorno tal e com argumentos tais".

Isso também pode acontecer quando existem variáveis globais que devem ser acessadas por funções em arquivos diferentes. Para isso existem a expressão "extern" ao declarar uma variável. Isso possibilita que exista somente uma variável com determinado nome e não várias (declaradas em cada arquivo). Funciona da seguinte maneira:
Você declara a variável como : "int a" em um arquivo
E nos outros você diz ao compilador: "hey, existe essa mesma variável em algum outro lugar, deixa pro link editor que ele vai encontrar". Isso é feito pela expressão "extern".

Resumindo, o processo de compilação traduz o código-fonte para código-objeto, isso se faz arquivo por arquivo, assim como o pré-processamento. Entretanto quando a compilação está acontecendo nem todas as variáveis e funções utilizadas estão no arquivo em questão, isso faz com que o compilador deixe "lacunas" no arquivo objeto que serão posteriormente conectadas pelo link-editor.

O processo de link-edição:

O processo de link-edição tem como matéria prima o código objeto e como produto o código de máquina. O trabalho dele é pegar todos os arquivos de código objeto e juntá-los, interligando as variáveis declaradas com extern com a variável declarada normalmente e também interligando as funções utilizadas em um arquivo e definidas em outro. Além disso, ele posiciona corretamente a função main dentro do arquivo, o que faz ela ser executada por primeiro.

Existe ainda mais um pequeno conceito a ser aprendido antes de podermos botar a mão na massa:

A diferença entre declaração e definição.

Uma declaração diz ao compilador que existe uma variável ou função com aquele nome, mas ainda não explica exatamente o que ela faz nem separa espaço na memória para ela, serve exatamente para dizer ao compilador deixar pra lá que em algum outro lugar isso vai ser encontrado. A declaração de uma função é feita pela utilização de protótipos:

int soma(int a, int b); (Perceba que está presente somente o nome da função e como chamá-la, mas não o código)

Já a declaração de uma variável utiliza a expressão extern:

extern int a, isso diz que algum outro lugar a variável vai ser definida e que nesse arquivo a variável global "a" é a mesma que a daquele outro.

Já a definição define o que uma função faz ou diz que o espaço para uma variável deve ser guardado na memória.

A definição de uma função é feita como de praxe:

int soma(int a, int b){
return a+b;
}

A definição de uma variável também:

int a; //Isso diz ao compilador: Guarda um espaço na memória de tanto espaço que eu vou usar nesse arquivo.

Com essas informações, vocês podem ter uma visão mais geral do que exatamente acontece na separação de arquivos. Mas ainda pode estar muito nebuloso como fazê-lo. Vou tentar então clarear as coisas. Mas agora tenho que sair e mais tarde volto para explicar em outro post. Também quero dar uma lida nesse tópico para tentar melhorar o texto..

Valeu pessoal!
Emilio Eduardo
Bit
 
Mensagens: 44
Registrado em: 13 Jan 2009 19:07

Mensagempor RobL » 13 Jan 2013 10:48

O estilo está muito bom, está bem acessível para um primeiro contato com o assunto.

Se der para continuar, evidencie bem a questão de declaração e não definição em arquivos cabeçalhos, etc e demais forma de organização.

Muito boa contribuição. Valeu pela paciência.
RobL
Dword
 
Mensagens: 1546
Registrado em: 20 Fev 2007 17:56


Voltar para PIC

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

cron

x