Programação Modular em C

funções X macros compilação condicional Diagnóstico

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.

Programação Modular
Figura 1: Decomposição funcional de uma aplicação.

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

Imagem destacada.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
2 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Guilherme Abraham
Guilherme Abraham
16/01/2017 10:21

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 […]

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Guilherme Abraham
16/01/2017 19:03

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/

Home » Software » Linguagem C » Programação Modular em C

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste:
Nenhum resultado encontrado.