# C estruturado e ferramentas

De C++ para C, módulos com cabeçalhos, Makefile, Git e Doxygen no Minix.

Página: https://resumos.rgo.pt/cadeiras/lc/c-estruturado/

Em LC programas em C, não em C++. Desaparecem as classes, as referências, o `new` e o `iostream`. Em troca ganhas controlo direto sobre a memória e um conjunto pequeno de ferramentas que vais usar em todos os trabalhos: o compilador `cc` (CLANG), o `make`, o `ar` para bibliotecas, o Git e o Doxygen para documentar. Esta página faz a tradução e monta o posto de trabalho.

## De C++ para C

As diferenças que vais sentir na primeira semana:

*   Sem classes: dados e funções vivem separados. Onde tinhas um objeto com métodos, tens agora uma `struct` mais funções que a recebem por apontador.
*   Sem `new` e `delete`: a alocação dinâmica faz-se com `malloc` e `free` da `stdlib.h`, e a memória continua a ser problema teu.
*   Sem `iostream`: entrada e saída com `printf` e `scanf` da `stdio.h`, com formatos explícitos (`%d`, `%x`, `%c`).
*   Sem referências e sem sobrecarga: passagem por valor ou por apontador, e cada função tem um nome único.

O resto do C++ que conheces (apontadores, `struct`, controlo de fluxo, aritmética de bits) transfere-se sem mudanças. Se os apontadores ainda tremem, revê-os antes de tocares em registos de hardware.

Experimenta a alocação dinâmica, que é onde quase todos os erros de memória começam. Este programa aloca um vetor de 5 inteiros, soma-o e liberta-o; a saída é `soma=150`.

```c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n = 5;
    int *v = malloc(n * sizeof(int));
    if (v == NULL) return 1;
    for (int i = 0; i < n; i++) v[i] = (i + 1) * 10;
    int soma = 0;
    for (int i = 0; i < n; i++) soma += v[i];
    printf("soma=%d\n", soma);
    free(v);
    return 0;
}
```

Repara nas três obrigações que o C++ escondia: verificar se o `malloc` devolveu `NULL`, libertar com `free` no fim e nunca tocar no vetor depois de libertado. Falhar a terceira corrompe memória silenciosamente, sem erro imediato que te avise.

## Módulos com cabeçalhos

Um módulo é um par de ficheiros: o cabeçalho `.h` declara o que o módulo oferece, o `.c` implementa. Quem usa o módulo inclui só o `.h`:

```
/* timer.h */
#ifndef TIMER_H
#define TIMER_H

int timer_set_frequency(unsigned long freq);
int timer_subscribe_int(void);

#endif
```

A guarda `#ifndef` impede inclusões duplas: sem ela, dois `#include` do mesmo cabeçalho duplicam as declarações e a compilação parte. Cada `.c` inclui o seu `.h` primeiro e depois as bibliotecas de que precisa. O diagrama resume as dependências: o `.c` implementa o que o `.h` declara, e o `main.c` só vê o `.h`.

![Diagrama de dependências de um módulo: timer.h declara, timer.c implementa, main.c inclui timer.h.](https://resumos.rgo.pt/cadeiras/lc/c-estruturado/figura-1.svg)

Esta disciplina parece burocracia até ao dia em que o projeto tem seis periféricos e cada um vive no seu módulo.

## A pilha e as chamadas de funções

Cada chamada de função reserva na pilha um **registo de ativação** (_stack frame_) com tudo o que a função precisa para correr e regressar: os argumentos do chamador, o endereço de retorno, o apontador do registo anterior e as variáveis locais.

![Pilha de um registo de ativação, de cima para baixo: argumentos, endereço de retorno, apontador guardado e variáveis locais.](https://resumos.rgo.pt/cadeiras/lc/c-estruturado/figura-2.svg)

Perceber esta pilha explica três fenómenos que vais encontrar. Primeiro, variáveis locais não inicializadas contêm o lixo que lá estava: ninguém limpa o registo ao entrar. Segundo, devolver o endereço de uma variável local é um erro clássico, porque o registo morre no retorno e o apontador fica a apontar para memória reutilizada. Terceiro, recursão funda demais esgota a pilha (_stack overflow_), porque cada chamada empilha um registo novo sem libertar os anteriores.

## Compilar com um Makefile

Compilar à mão cada ficheiro com o `cc` funciona uma vez. À segunda, escreve um `Makefile` que diga como construir cada objeto e como os ligar:

```
CC = cc
CFLAGS = -Wall -Wextra

proj: main.o timer.o kbd.o
	$(CC) $(CFLAGS) -o proj main.o timer.o kbd.o

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f *.o proj
```

As linhas de receita começam obrigatoriamente por tabulação, não por espaços: é o erro mais clássico do primeiro Makefile. Com isto, `make` recompila só o que mudou e `make clean` limpa os objetos.

Quando um módulo estabiliza, empacota-o numa **biblioteca estática** com o `ar` em vez de recompilar o `.o` sempre. A receita junta o objeto num `.a` e o programa liga-o com `-l`:

```
libtimer.a: timer.o
	ar rcs libtimer.a timer.o

proj: main.o libtimer.a
	$(CC) $(CFLAGS) -o proj main.o -L. -ltimer
```

O `ar rcs` cria o arquivo (`c`), insere o objeto substituindo versões antigas (`r`) e escreve o índice (`s`). O `-L.` diz onde procurar bibliotecas e o `-ltimer` pede a `libtimer.a`. A biblioteca copia o código para dentro do executável na ligação: o programa final não precisa do `.a` para correr.

Como ler um erro do clang

Compila este programa sem o `#include <stdio.h>` e o clang responde com `use of undeclared identifier 'printf'` mais uma sugestão. O método é sempre o mesmo: lê a primeira linha do erro (ficheiro e número da linha), percebe o que o compilador não encontrou e corrige só isso antes de recompilar. Resolve os erros de cima para baixo, porque um erro a montante costuma arrastar os de baixo.

## O compilador em cada ambiente

Os comandos são os mesmos, o ambiente muda. Escolhe o separador do teu caso.

No Minix compilas com o `cc`, que é o CLANG. Verifica a versão e compila com avisos no máximo, porque os avisos do CLANG apanham máscaras deslocadas e formatos errados antes de correres o programa.

```
cc --version
cc -Wall -Wextra -o teste teste.c
```

Em Linux para treinar C fora da máquina virtual usa o GCC, com as mesmas flags. O código dos módulos compila igual; só as chamadas específicas do Minix (como a subscrição de interrupções) é que não existem cá fora.

```
gcc --version
gcc -Wall -Wextra -o teste teste.c
```

## Git, diff e Doxygen

Cada trabalho vive num repositório Git. A sequência mínima que tens de saber de cor:

```
git init
git add timer.c timer.h
git commit -m "temporizador: polling a ler o estado"
git status
git log --oneline
```

O `status` antes de cada commit confirma o que entra; o `log --oneline` mostra o histórico numa linha por commit. Quando trabalhares em grupo, `pull` antes de começar e `push` ao acabar.

Para partilhar uma correção sem repositório comum, gera-se um remendo com `diff` e aplica-se com `patch`:

```
diff -u timer_velho.c timer_novo.c > correcao.patch
patch -p1 < correcao.patch
```

O formato `-u` mostra o contexto em volta de cada mudança, e o `-p1` descasca o primeiro nível de diretórios do caminho no remendo. Antes de aplicar, lê o `.patch`: cada bloco diz que linhas saem (com `-`) e que linhas entram (com `+`).

A documentação escreve-se no próprio código em formato Doxygen, com `/** ... */` antes de cada função pública a explicar parâmetros e retorno. Gerar a documentação passa a ser comandos, não um relatório escrito à parte no fim do semestre:

```
doxygen -g
doxygen Doxyfile
```

O primeiro comando cria um `Doxyfile` com todas as opções comentadas; ajustas nele o nome do projeto e os diretórios de entrada, e o segundo gera as páginas HTML. Abre o `index.html` gerado e confirma que cada função pública aparece com os seus parâmetros documentados.

O que o C não faz por ti

Não há verificação de limites em vetores, não há inicialização automática de variáveis e um apontador solto corrompe memória silenciosamente. Quando o programa se comportar de forma impossível, suspeita primeiro de escrita fora de limites e de variável não inicializada, antes de culpares o hardware.
