Enviar um café pro programador

Pode me ajudar a transformar cafeína em código?

Headers (cabeçalhos) - O que são, para que servem, como criar e usar seus arquivos .h

Já parou pra pensar melhor na primeira linha de código de todos seus programas em C?

Mais especificamente o "stdio.h", "stdlib.h", "math.h" etc.
Que ".h" é esse? Para que serve? Como usar ?

É sobre estes arquivos e sua utilização que iremos falar neste tutorial de nossa apostila de C.
Além do mais, este tutorial é uma continuação do tutorial sobre Módulos em C, portanto é imprescindível que você o tenha lido antes, pois vamos dar continuidade a ele.
Clique aqui e saiba como obter seu certificado de programação C 

Cabeçalhos ou Headers - Arquivo .h

Uma das principais características de programadores é a curiosidade.
Então, para iniciar esse tutorial de nossa apostila, vamos pedir que você procure e abra alguns arquivos headers, ou seja, arquivos que tem a extensão .h, como o stdio.h e o stdlib.h, que tanto usamos no decorrer de nosso curso.

Se abrir os arquivos, verá que ele contém diversos protótipos de funções, muitos comentários e outras coisas.
Mas código C, como estamos estudando, verá muito pouco.

Vamos usar os cabeçalhos para organizar algumas informações, assim como fizemos com os módulos.
Nos headers, esses arquivos .h, iremos colocar a declaração de todas as funções, bem como vamos comentar bem cada uma delas, explicando o que são, para que servem, que parâmetros recebem e o que retornam.
Macros, que é um assunto que veremos mais à frente em nosso curso, também é bastante usado nos arquivos headers.

Os arquivos .h são uma espécie de 'intermediário', entre seu programa e os módulos.
Nos cabeçalhos é onde explicamos o que cada função definida nos módulos faz, ou seja, o header é uma interface de comunicação.

Vamos ver, até o final deste tutorial, que apenas lendo o header 'calculadora.h', saberemos como usar tudo que o módulo 'calculadora.c' tem a oferecer, sem nos importar com a maneira na qual ele foi desenvolvido.
Ou seja, ao invés de ler o módulo com centenas de linhas de código, vamos apenas ter contato com o header, um arquivo bem pequeno, simples e explicativo.

Como usar um cabeçalho ou header em C

No artigo passado, sobre o uso de módulos para a divisão e organização de nosso código, nosso projeto ficou dividido em dois arquivos:

main.c
calculadora.c

Na main.c, colocamos a função main() e os protótipos de todas as funções existentes em 'calculadora.c'.

Esse protótipos são os cabeçalhos das funções, os headers.

Se não se lembra de como criar um protótipo, leia o seguinte tutorial de nossa apostila:
Como criar protótipos de funções em C

Se notarmos a nossa função main.c, ela possui os seguintes cabeçalhos de funções:
  1. float soma(float x, float y);
  2. float subtracao(float x, float y);
  3. float produto(float x, float y);
  4. float divisao(float x, float y);
  5. float quadrado(float x);
  6. float cubo(float x);
  7. int restoDivisao(int x, int y);
  8. int par(int x);
  9. int multiplo(int x, int y);
  10. int primo(int x);
  11. int* divisores(int x);
  12. void menu();
  13. void executa(int opcao);

Vimos que deixa o arquivo main.c bem pequeno e enxuto.
Mas convenhamos, é um aplicativo simples. Imagine que você fosse fazer um jogo em C, com módulos das funções de áudio, imagem, fases, personagens, jogabilidade etc.
Seu módulo main.c ficaria simplesmente gigante e impossível de se suar, tamanho número de cabeçalhos.

E é aí que entram os arquivos .h

Vamos colocar neles todos os cabeçalhos, e faremos mais: vamos colocar descrições simples, diretas e completas. Essa parte da descrição será importante, principalmente no quesito profissional.
Quando algum cliente adquire um código-fonte em C ou um programador começa a estudar um projeto, ele não vai ler linha por linha as dezenas, centenas ou milhares módulos, para saber o que o programa faz.
Ao invés disso, eles irão ler as bibliotecas, os headers, pois lá possuem descrições simples de como usar as funcionalidades do software.

Agora que já sabemos o motivo do uso dos headers, vamos criar um dito cujo.

Como criar um arquivo header .h

Vamos usar o nosso projeto, do tutorial passado, onde temos o arquivo 'main.c' e o 'calculadora.c'.
Agora vamos criar nosso cabeçalho, com as funções.

Para isso, haja como se fosse criar mais arquivo de módulo:
File -> New -> Empty File

Vai ser perguntado se deseja adicionar esse arquivo ao seu projeto.
Diga que sim, e dê o nome 'calculadora.h'

É sempre bom que dê o mesmo nome do módulo, pois o módulo e o header trabalham sempre em conjunto.
Um vai explicar tudo (.h) e o outro vai implementar (.c).

Note que ao dar um nome qualquer, com a extensão .h, o Code::Blocks vai criar uma seção chamada 'Headers', no menu direito, e vai colocar seu header lá.


Como criar um cabeçalho .h em C
Projeto no Code Blocks, com módulos e headers

Agora, vamos pegar todas as declarações de funções que estão na 'main.c', que são os protótipos de todas as funções contidas no módulo 'calculadora.c' e colocar no nosso header 'calculadora.h'.

Agora precisamos que o módulo 'main.c' saiba da existência desses protótipos.
Para isso, vamos incluir o header, adicionando:
#include "calculadora.h"

É como se disséssemos "Hey Code Blocks, inclua tudo que tem no arquivo calculadora.h neste main.c, pois está tudo lá agora e não quero reescrever". Pronto, é como se nada tivesse mudado (à rigor, em baixo nível, nada mudou mesmo, pois no processo de compilação e linkagem, vai tudo virar um arquivo só, o executável).

Veja como ficou limpo, o nosso módulo main.c:

Headers e cabeçalhos em C


No #include, colocamos apenas "calculadora.h" pois este arquivo de header está no mesmo diretório/pasta do main.c
Se criar uma pasta 'cp' e colocar o header lá, faça:
#include "cp/calculadora.h" , se estiver no Linux ou
#include "cp\calculadora.h" , se estiver no Windows.

Isto pode ser útil quando for criar um projeto grande, que tenha vários arquivos de cabeçalhos, e seja preciso organizá-los em pastoas/diretórios.

Caso você coloque os headers na pasta padrão de seu sistema/compilador/IDE, pode colocar somente:
#include <calculadora.h>

No Linux, essa pasta fica no diretório "/usr/include", no Windows ficará na pasta que você instalou seu compilador.

E pronto. Já pode rodar nosso mini-projeto.
Em termos de código, ele já está pronto, dividido e organizado.

Mas ainda falta aprender como criar um header mais padrão, um cabeçalho mais organizado.

Como organizar um header

Agora vamos aprender como deixar nosso arquivo de cabeçalho mais legível, nos aproximar da maneira que eles são realmente usados.

O trabalho aqui não envolve programação, mas sim bom senso e organização, que são tão essenciais quanto codificar. Imagine quantos headers, módulos e linhas de códigos existem em um sistema operacional.
São milhões.

Fora a dificuldade de saber programar um sistema tão complexo, é impossível tem um arquivo com milhões de linhas, nos perderíamos rápido. Ter só um header também não ajuda muito.
O que se faz é ter vários diretórios, com vários headers e módulos. Tudo bem separado e organizado.

E dentro desses headers, é onde será 'dito', numa linguagem humana, o que cada seção desta de código será responsável. Isso evita que seja necessário ler o código C de outros programadores, o que geralmente é um trabalho cansativo e difícil.

Vamos começar simplesmente dizendo o que aquele header faz, que é explicar as funções existentes no módulo calculadora.c, responsável por uma série de operações matemáticas:

/* 
** Cabeçalhos das funções implementadas no módulo calculadora.c ,
** que servem para realizar diversos cálculos matemáticos
*/

A seguir, vamos explicar o que cada função faz.
Nosso objetivo aqui é que uma pessoa que nunca viu o módulo 'calculadora.c' consiga usar as funções existentes nele, apenas lendo este arquivo de cabeçalho.
Portanto, devemos dizer o que cada função retorna, através dos dados recebidos.

Header 'calculadora.h':

  1. /*
  2. ** Cabeçalhos das funções implementadas no módulo calculadora.c ,
  3. ** que servem para realizar diversos cálculos matemáticos
  4. */
  5. /*
  6. ** Retorna o valor da soma de dois números recebidos
  7. */
  8. float soma(float x, float y);
  9. /*
  10. ** Retorna o valor da diferença de dois números recebidos
  11. */
  12. float subtracao(float x, float y);
  13. /*
  14. ** Retorna o produto de dois números
  15. */
  16. float produto(float x, float y);
  17. /*
  18. ** Retorna o resultado da divisão do número x pelo y,
  19. ** exceto quando y for 0
  20. */
  21. float divisao(float x, float y);
  22. /*
  23. ** Retorna o quadrado de um número
  24. */
  25. float quadrado(float x);
  26. /*
  27. ** Retorna o cubo de um número
  28. */
  29. float cubo(float x);
  30. /*
  31. ** Resto da divisão do número x pelo y
  32. */
  33. int restoDivisao(int x, int y);
  34. /*
  35. ** Retorna 1 se o número recebido for par, e 0 caso contrário
  36. */
  37. int par(int x);
  38. /*
  39. ** Retorna 1 caso x seja múltiplo de y e 0 caso contrário
  40. */
  41. int multiplo(int x, int y);
  42. /*
  43. ** Retorna 1 caso x seja um número primo e 0 caso contrário
  44. */
  45. int primo(int x);
  46. /*
  47. ** Retorna um vetor com todos os divisores do número x
  48. */
  49. int* divisores(int x);
  50. /*
  51. ** Função responsável por exibir o menu de opções
  52. */
  53. void menu();
  54. /*
  55. ** Esta função executa a opção escolhida no menu
  56. */
  57. void executa(int opcao);


Pronto, agora qualquer pessoa pode fazer uso das funções matemáticas que você criou, sem sequer imaginar como você implementou isso.
Isso é muito comum quando baixamos bibliotecas para usar em nossos projetos.
Quando queremos usar elementos gráficos em C, temos que baixar vários arquivos, entre módulos e headers.

Porém, não queremos saber como foram criados, somente usar.
Pra isso, importamos os headers em nossos projetos, e utilizamos suas funcionalidades. E lemos a documentação para saber como usar cada uma desta funcionalidade.

Exercício proposto

Você foi contratado por um laboratório de pesquisas científicas como programador C.
Eles querem que você implemente uma série de funções para se trabalhar com matrizes.

Te pediram que seu projeto forneça as seguintes funcionalidades:
cria: operação que cria uma matriz de dimensão m por n;
libera: operação que libera a memória alocada para a matriz;
acessa: operação que acessa o elemento da linha i e da coluna j da matriz;
atribui: operação que atribui o elemento da linha i e da coluna j da matriz;
linhas: operação que devolve o número de linhas da matriz;
colunas: operação que devolve o número de colunas da matriz.

Pronto. Te disseram só isso.
Obviamente, eles tem outras coisas para fazer e não querem nem saber como você vai implementar estas funções, vão querer apenas usá-las. Portanto, crie um módulo que implemente tais funções e um header, onde você vai explicar para eles como usar cada uma destas funções.

Este exercício será resolvido e utilizado para explicar os conceitos de TAD - Tipo Abstrato de Dados.
Iremos usar o seguinte material da PUC-RJ:
http://www.ic.unicamp.br/~ra069320/PED/MC102/1s2008/Apostilas/Cap09.pdf

É um capítulo de uma excelente apostila, que explica os conceitos de módulos e headers, vistos aqui, bem como os conceitos de TAD, que abordaremos em breve.

4 comentários:

Dream Ball disse...

Olá, meu nome é wesley, e programo em Dev C++, como faço esse processo nele ?
Ass.: Wesley Thyago

Apostila C Progressivo disse...

Olá Wesley,

Deve ser um procedimento parecido no Dev (de se criar um projeto).

Há muitos anos, quando iniciei, o projeto do Dev-C++ já tinha sido abandonado, e naquela é poca ele já estava cheio de erros, problemas e bem atrasado.

Por isso não trabalho mais com Dev C++, e sim com Code::Blocks, que ensinamos a baixar e instalar nas primeiras aulas de nosso curso.

DoesntMatter disse...

É tarde demais para comentar?

Eu to com umas dúvidas.
Por que esse .h que você mostrou é tão mais limpo?
Quero dizer, se pegarmos um arquivo .h de alguma biblioteca por aí vemos palavras como "extern", "_stdcall", diretivas como #pragma, #include_next, e as vezes vemos trechos assim:

#ifdef __cplusplus
extern "C" {
#endif

As vezes vejo typedefs para funções, e acho isso muito estranho.
Pior é que eu não acho material explicando isso e, como sou auto-didata, fica complicado entender e acabo me desanimando.

Obrigado desde já

Anônimo disse...

Cara, o código é limpo assim porque o programa da calculadora não exige uma arquitetura muito rebuscada. Se você fizesse um programa com vários arquivos, variáveis globais, macros e várias outras coisas provavelmente você iria precisar desse conhecimento extra de sintaxe. Se você procura entender mais sobre isso, vai ser difícil achar material em português. No caso, se você souber inglês, dá pra aprender bastante lendo fóruns, stackoverflow e até mesmo a documentação, que com certeza é completa e explica como usar tudo isso que você citou. Caso contrário, você vai precisar procurar bastante algo em português ou então pedir ajuda pra alguém que possa te ensinar isso. Boa sorte.