Olá, caro leitor! Este artigo reúne informações importantes sobre programação modular que já foram apresentadas em outros artigos. O principal objetivo é fundamentar os conceitos necessários para organizar uma aplicação em linguagem C em um conjunto de módulos funcionais.
A Linguagem C
A característica especial de uma linguagem estruturada é a compartimentalização do código e dos dados, isto é, a capacidade de seccionar e encapsular partes do programa. Diante disso, o principal componente em C que possui essa característica é a função. Com a função é possível modularizar um programa, permitindo codificar, separadamente, uma determinada tarefa. Além disso, tal procedimento traz algumas vantagens como a reutilização de código, compilação individual de código, e a estrutura do código fica mais clara, por exemplo.
O Design Estruturado
O projeto de um programa em C segue uma abordagem Top-Down. Essa abordagem é caracterizada por estruturar o projeto a partir da rotina de nível mais alto até as rotinas de nível mais baixo. Um esboço de uma determinada parte do programa começa com uma descrição geral e caminha em direção da particularização. Vale a pena conferir o artigo do Felipe Lavratti sobre abordagem Top-Down.
Diante disso, o design estruturado corresponde a uma decomposição funcional de uma aplicação num conjunto de módulos bem definidos. Esses módulos cooperam para desempenhar a funcionalidade definida pela aplicação, conforme ilustrado na Figura 1.
Programação Estruturada: Um método de escrever um programa que utiliza (1) análise Top-Down para solução de problemas, (2) modularização para estruturar e organizar o programa, e (3) estruturação do código em módulos individuais [2].
Módulos
Um módulo na linguagem C pode ser criado utilizando arquivos de cabeçalho e de implementação. Abaixo segue a definição de cada tipo de arquivo:
Arquivo de cabeçalho: Os arquivos ‘.h’ são utilizados para especificar assinatura de funções, definições de constantes, tipos de dados criados pelo usuário etc. De modo geral, os arquivos de cabeçalho têm como função definir a interface de um módulo.
Arquivo de implementação: Os arquivos ‘.c’ implementam as funções definidas na interface. De modo geral, esses arquivos são compostos por diversas funções e estruturas de dados utilizadas internamente.
Para criar aplicações seguras que não entrem em conflito com outros módulos, os conceitos apresentados sobre modificadores de armazenamento e o pré-processador podem ser aplicados. Exemplo disso é a utilização de header guards, outro é a utilização de modificadores static e extern.
O conceito de header guards pode ser visto aqui. Basicamente, a aplicação de header guards é realizada para evitar que o código presente no arquivo de cabeçalho seja copiado para o fonte que fez sua inclusão. Já os modificadores podem ser empregados para limitar o escopo de funções e variáveis que são utilizadas somente dentro do módulo, tornando “público” somente o que é necessário.
Aplicação Modular na Prática
Esses conceitos podem ser vistos no módulo slist criado pelo Felipe Neves e apresentado neste artigo. Todas as funções e definições de dados pertencentes ao módulo estão dentro de uma estrutura de compilação condicional.
* @brief single linked list interface file
*/
#ifndef __SLIST_H_
#define __SLIST_H_
#include <stdbool.h>
/* single linked list data structure */
typedef struct slist_s {
void *data;
struct slist_s *next;
}slist_t;
/* single linked list head data structure */
typedef struct {
slist_t *first;
int noof_elements;
}slist_info_t;
/**
* @brief insert an container in the slist
*/
int slist_insert(slist_info_t *s, slist_t *item, void *data);
/**
* @brief gets data from a specified position with optional remotion
*/
int slist_get_data_from_position(slist_info_t *s, int position, void *data, bool remove);
/**
* @brief gets data from head with optional remotion
*/
int slist_get_data_from_head(slist_info_t *s, void *data, bool remove);
/**
* @brief gets data from tail with optional remotion
*/
int slist_get_data_from_tail(slist_info_t *s, void *data, bool remove);
/**
* @brief deletes all the items in current list
*/
int slist_clean(slist_info_t *s);
/**
* @brief get the current number of entries on list
*/
int slist_get_noof_entries(slist_info_t *s);
/**
* @brief linked list instantiation macro:
*/
#define SLIST_DECLARE(name) \
slist_info_t name = { \
.first = (void *)0, \
.noof_elements = 0, \
}
#endif /* SLIST_H_ */
Já o arquivo fonte contém a implementação das funções definidas e também funções internas do próprio módulo. Observe que para essas funções internas o modificador static foi utilizado para limitar seu escopo. Isso também pode ser aplicado em variáveis internas do módulo.
/**
* @brief single linked list interface file
*/
#include <string.h>
#include <stdlib.h>
#include "slist.h"
/*
* Internal functions
*/
/**
* @brief makes a link of a outside incoming item to a list
*/
static void slist_link(slist_info_t *s, slist_t *item)
{
}
/**
* @brief breaks the link of specified list item
*/
static void slist_unlink(slist_info_t *s, slist_t *item, int position)
{
}
/*
* Public functions
*/
int slist_insert(slist_info_t *s, slist_t *item, void *data)
{
}
int slist_get_data_from_position(slist_info_t *s, int position, void *data, bool remove)
{
}
int slist_get_data_from_head(slist_info_t *s, void *data, bool remove)
{
}
int slist_get_data_from_tail(slist_info_t *s, void *data, bool remove)
{
}
int slist_clean(slist_info_t *s)
{
}
int slist_get_noof_entries(slist_info_t *s)
{
}
Dessa maneira, tudo que é relativo ao módulo fica encapsulado, isto é, restrito ao escopo do arquivo fonte.
Referências
[1] Livro: C, completo e total – 3ª edição revista e atualizada. Herbert Schildt. [2] John Dalbey – Structured Programming.Figura 1 – https://www.tenouk.com/Module4.html






Excelente artigo. Uma coisa que sinto falta nas disciplinas de engenharia de software são técnicas para projetos que não sejam orientados a objeto. Vou ler os links disponibilizados nesse artigo mas já deixo uma sugestão de uma série focada em técnicas pra projetos de software no paradigma estruturado/funcional.
Vou tomar a liberdade de apontar 2 erros no texto:
[…] Um método de escrever um programa que utiliza
(1) (anÁlise) Top-Down para solução de problemas […]
[…] Basicamente, a aplicação de header guards é realizada para evitar (quer) o
código presente no arquivo […]
Olá, Guilherme. Obrigado pelo retorno!
Pois é, as linguagens estruturadas acabam ficando apenas nas matérias de introdução e restritas ao console preto…
Eu escrevi outro artigo que também trata esse tipo de assunto, mas ficou vinculado a série de ponteiros. Se quiser conferir: https://embarcados.com.br/ponteiro-em-c-tipo-de-dado-abstrato/