Programação de Computadores

Bibliotecas e Modularização em C

Professor: Gabriel Soares Baptista

Objetivos

  • Compreender o que e uma biblioteca em C.
  • Diferenciar bibliotecas padrao de bibliotecas proprias.
  • Entender o papel da diretiva #include.
  • Diferenciar arquivos .h e .c.
  • Utilizar corretamente header guards.
  • Compilar projetos com varios arquivos usando GCC.
  • Construir uma biblioteca propria simples.

Por que modularizar?

  • Em exercicios pequenos, um unico main.c pode ser suficiente.
  • Em programas maiores, isso rapidamente vira um problema.
  • Funcoes ficam misturadas, o arquivo cresce demais e o reuso piora.
  • Modularizar significa dividir o sistema em partes menores e organizadas.
Vantagem

Um modulo bem feito pode ser reutilizado em varios programas sem copiar e colar codigo.

O que e uma biblioteca?

  • Uma biblioteca e um conjunto de funcoes, tipos, constantes ou macros prontos para uso.
  • Em C, podemos usar:
    • bibliotecas padrao, como stdio.h, string.h e math.h;
    • bibliotecas proprias, criadas dentro do projeto.
  • A ideia central e reaproveitar codigo e organizar melhor o programa.

Bibliotecas padrao x proprias

Bibliotecas padrao

  • Ja fazem parte do ecossistema da linguagem.
  • Sao usadas para tarefas comuns.

Exemplo:

  • stdio.h: entrada e saida.
  • math.h: calculos matematicos.

Bibliotecas proprias

  • Sao criadas pelo programador.
  • Organizam funcoes do proprio sistema.

A diretiva #include

  • #include e tratado pelo pre-processador.
  • Ele insere o conteudo do cabecalho no ponto onde a diretiva aparece.
  • Isso nao compila o programa sozinho.
  • O #include apenas torna declaracoes visiveis para o arquivo atual.
#include <stdio.h>
#include "calc.h"

< > e " "

  • #include <stdio.h>

    • normalmente usado para bibliotecas padrao;
    • o compilador procura nos diretorios padrao.
  • #include "calc.h"

    • normalmente usado para bibliotecas do projeto;
    • o compilador procura primeiro no diretorio local.

Bibliotecas padrao importantes

<stdio.h>

  • Biblioteca de entrada e saida padrao.
  • Muito usada para ler dados do teclado e imprimir resultados na tela.

<stdio.h>

printf()

  • Imprime valores formatados na tela.
#include <stdio.h>

int main(){
    printf("Ola\n");
    return 0;
}

<stdio.h>

scanf()

  • Le valores digitados pelo usuario.
#include <stdio.h>

int main(){
    int x;
    scanf("%d", &x);
    printf("Valor lido: %d\n", x);
    return 0;
}

<stdio.h>

fgets()

  • Le uma linha de texto.
  • E melhor que scanf("%s", ...) quando queremos aceitar espacos.
#include <stdio.h>

int main(){
    char nome[30];
    fgets(nome, 30, stdin);
    printf("Texto lido: %s", nome);
    return 0;
}

<stdio.h>

puts()

  • Imprime uma string e quebra linha automaticamente.
#include <stdio.h>

int main(){
    puts("Fim");
    return 0;
}

Bibliotecas padrao importantes

<string.h>

  • Biblioteca usada para manipulacao de strings.

<string.h>

strlen()

  • Mede o tamanho da string.
#include <stdio.h>
#include <string.h>

int main(){
    printf("%zu\n", strlen("abc"));
    return 0;
}

<string.h>

strcpy()

  • Copia uma string para outra.
#include <stdio.h>
#include <string.h>

int main(){
    char origem[] = "Casa";
    char destino[20];
    strcpy(destino, origem);
    printf("%s\n", destino);
    return 0;
}

<string.h>

strcat()

  • Concatena uma string ao final de outra.
#include <stdio.h>
#include <string.h>

int main(){
    char texto[20] = "Linguagem";
    strcat(texto, " C");
    printf("%s\n", texto);
    return 0;
}

<string.h>

strcmp()

  • Compara duas strings.
  • Retorna 0 se forem iguais.
#include <stdio.h>
#include <string.h>

int main(){
    int resultado = strcmp("abc", "abd");
    printf("%d\n", resultado);
    return 0;
}

Bibliotecas padrao importantes

<math.h>

  • Biblioteca usada para operacoes matematicas.

<math.h>

sqrt()

  • Calcula raiz quadrada.
#include <stdio.h>
#include <math.h>

int main(){
    printf("%.1f\n", sqrt(25.0));
    return 0;
}

<math.h>

pow()

  • Calcula potencia.
#include <stdio.h>
#include <math.h>

int main(){
    printf("%.1f\n", pow(2, 3));
    return 0;
}

<math.h>

fabs()

  • Calcula valor absoluto de numeros reais.
#include <stdio.h>
#include <math.h>

int main(){
    printf("%.1f\n", fabs(-4.5));
    return 0;
}

<math.h>

PI

  • Podemos obter pi com acos(-1.0).
#include <stdio.h>
#include <math.h>

int main(){
    double pi = acos(-1.0);
    printf("%.6f\n", pi);
    return 0;
}

<math.h>

Euler

  • Podemos obter o numero de Euler com exp(1.0).
#include <stdio.h>
#include <math.h>

int main(){
    double euler = exp(1.0);
    printf("%.6f\n", euler);
    return 0;
}

Compilacao com math.h

Em muitos sistemas Linux e Unix, usar #include <math.h> nao basta.

Tambem e necessario ligar a biblioteca matematica com -lm:

gcc programa.c -o programa -lm

Se o projeto tiver mais de um arquivo:

gcc main.c calc.c -o programa -lm

Outras bibliotecas uteis

<stdlib.h>

  • atoi() converte string para inteiro.
#include <stdio.h>
#include <stdlib.h>

int main(){
    printf("%d\n", atoi("123"));
    return 0;
}

Outras bibliotecas uteis

  • rand() gera numero pseudoaleatorio.
#include <stdio.h>
#include <stdlib.h>

int main(){
    printf("%d\n", rand() % 100);
    return 0;
}

Outras bibliotecas uteis

  • srand() inicializa a semente do gerador.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
    srand(time(NULL));
    printf("%d\n", rand() % 100);
    return 0;
}

<ctype.h>

  • toupper() maiuscula.
#include <stdio.h>
#include <ctype.h>

int main(){
    printf("%c\n", toupper('a'));
    return 0;
}

<ctype.h>

  • tolower() minuscula.
#include <stdio.h>
#include <ctype.h>

int main(){
    printf("%c\n", tolower('Z'));
    return 0;
}

<ctype.h>

  • isdigit() verifica digito.
#include <stdio.h>
#include <ctype.h>

int main(){
    printf("%d\n", isdigit('7'));
    return 0;
}

Arquivo .h x Arquivo .c

.h

  • Contem a interface do modulo.
  • Normalmente guarda prototipos de funcoes.

.c

  • Contem a implementacao.
  • E onde escrevemos o corpo das funcoes.

Header Guards

  • Evitam que o mesmo cabecalho seja incluido varias vezes na mesma compilacao.
#ifndef CALC_H
#define CALC_H

int soma(int a, int b);

#endif

Compilando varios arquivos

Se o projeto tiver:

  • main.c
  • calc.c
  • calc.h

um comando correto e:

gcc main.c calc.c -o programa

Se o cabecalho estiver em outra pasta, podemos usar -I:

gcc main.c src/calc.c -Iinclude -o programa

Erros comuns

  • Esquecer #include "calc.h".
  • Compilar apenas main.c.
  • Incluir .c no lugar de .h.
  • Esquecer header guards.
Erro tipico

Se a funcao foi declarada, mas o .c com a implementacao nao entrou na compilacao, e comum aparecer undefined reference.

Construindo nossa propria biblioteca

Vamos criar um modulo chamado calc.

Objetivo:

  • soma
  • media
  • maximo

Fluxo da atividade:

  1. Primeiro, vamos criar apenas o .h.
  2. Depois, implementaremos o .c funcao por funcao.
  3. Em cada etapa, veremos um exemplo de uso no main.

Passo 1 - Nosso calc.h

#ifndef CALC_H
#define CALC_H

int soma(int a, int b);
float media(int a, int b);
int maximo(int a, int b);

#endif
  • O .h apenas anuncia as funcoes.
  • Ainda nao ha implementacao aqui.

Implementem apenas o arquivo calc.h

Façam agora:

  • criem o arquivo calc.h;
  • adicionem os header guards;
  • escrevam os tres prototipos.

Ainda nao implementem o .c.

Implementem agora apenas soma

Façam no calc.c:

  • #include "calc.h";
  • implementem somente soma.

Depois, testem chamando soma() no main.

Exemplo de uso no main:

#include <stdio.h>
#include "calc.h"

int main(){
    printf("Soma: %d\n", soma(4, 7));
    return 0;
}

Implementem agora apenas media

Façam no calc.c:

  • implementem somente media;
  • testem a chamada no main.

Exemplo de uso no main:

#include <stdio.h>
#include "calc.h"

int main(){
    printf("Media: %.2f\n", media(4, 7));
    return 0;
}

Implementem agora apenas maximo

Façam no calc.c:

  • implementem somente maximo;
  • testem a chamada no main.

Exemplo de uso no main:

#include <stdio.h>
#include "calc.h"

int main(){
    printf("Maximo: %d\n", maximo(4, 7));
    return 0;
}

Biblioteca completa

Agora o arquivo calc.c fica assim:

#include "calc.h"

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

float media(int a, int b){
    return (a + b) / 2.0f;
}

int maximo(int a, int b){
    if(a > b)
        return a;
    return b;
}

main.c usando tudo

#include <stdio.h>
#include "calc.h"

int main(){
    printf("Soma: %d\n", soma(4, 7));
    printf("Media: %.2f\n", media(4, 7));
    printf("Maximo: %d\n", maximo(4, 7));
    return 0;
}

Compilacao:

gcc main.c calc.c -o programa

Expansao do modulo

Se quisermos crescer a biblioteca, basta:

  1. adicionar o prototipo no .h;
  2. implementar a funcao no .c;
  3. chamar a funcao no main.

Exemplo: minimo

int minimo(int a, int b);

Desafio

Agora, em duplas ou individualmente, criem uma nova biblioteca.

Opcao 1: vetor_util

  • soma_vetor(int v[], int n)
  • maior_vetor(int v[], int n)
  • media_vetor(int v[], int n)

Opcao 2: texto_util

  • contar_vogais(char str[])
  • inverter_string(char str[])
  • eh_palindromo(char str[])

Regras do desafio

  • um arquivo .h com header guards;
  • um arquivo .c com as implementacoes;
  • um main.c chamando cada funcao pelo menos uma vez;

Sempre que implementarem uma funcao, testem no main imediatamente.

Proximos Passos

  • Bibliotecas ajudam a organizar e reaproveitar codigo.
  • O .h anuncia o modulo.
  • O .c implementa o modulo.
  • O main usa o modulo.
  • O GCC precisa receber os arquivos corretos na compilacao.

Na proxima aula veremos como trabalhar com memoria dinamica em C.