Edição e Compilação C em UNIX

No processo de codificação de um software, são atividades básicas:

Além destas, também são atividades necessárias, sobretudo à medida em que os programas desenvolvidos crescem em tamanho e complexidade:

Neste módulo serão enumeradas algumas ferramentas disponíveis para auxiliar nas diversas etapas do desenvolvimento de programas C/C++ em plataformas UNIX, mais particularmente no mundo Linux. A maioria das ferramentas é software livre,

Edição de código

Para a edição de código-fonte pode-se optar por editores de código-fonte como Emacs, ou Vi, Jed, Kate (no ambiente KDE), Gedit (no ambiente Gnome), Atom e muitos outros. Pode-se também optar por ambientes de desenvolvimento integrado (IDEs) como Eclipse, Kdevelop, Anjuta, e diversos outros.

Qual é a melhor opção? Esta é uma pergunta difícil de responder, pois há muitas controvérsias...

Compilação

Existem vários compiladores C/C++ disponíveis livremente em ambiente Linux, mas o GCC (GNU Compiler Collection) é de longe o compilador mais popular e certamente um dos melhores. O GCC compreende compiladores de front-end e geradores de código para várias linguagens de programação (C, C++, ObjC, Fortran, Ada, Java, ...) em diversas plataformas. Além dele, sugere-se o compilador CLANG para C/C++/ObjC, pela qualidade do código gerado e suas mensagens de erro bem mais compreensíveis que as do GCC.

Iniciemos com o Hello world


#include <stdio.h>

int main ()
{
  printf ("Hello, world\n") ;
  return (0) ;
}

O GCC é um compilador que opera em linha de comando. Por default, o compilador realiza a compilação e ligação do(s) fonte(s), gerando um arquivo executável denominado ''a.out'' (que tem esse nome por razões históricas). Para executar esse arquivo basta invocá-lo, em uma linha de comando. O prompt é mostrado como ''~>''.

~> gcc hello.c
 
~> ls -l
-rwxr-xr-x 1 prof 11724 Set 28 16:25 a.out
-rw-r--r-- 1 prof 45 Set 28 16:13 hello.c
 
~> a.out
Hello, world

Pode-se redefinir o arquivo de saída com a opção ''-o'':

~> gcc -o hello hello.c
~> ./hello
Hello, world

É possível compilar diversos arquivos interdependentes simultaneamente, como mostra o seguinte exemplo:


int main ()
{
  escreva ("Hello, world\n") ;
  return (0) ;
}

#include <stdio.h>

void escreva (char *msg)
{
  printf ("%s", msg) ;
}

~> gcc -o hello hello.c escreva.c
~> ./hello
Hello, world

Também é possível efetuar somente a compilação dos arquivos com código-fonte, sem efetuar a ligação e a geração do executável. Nesse caso, são gerados os arquivos com o código-objeto (*.o) dos correspondentes arquivos de código fonte (*.c), como mostra o exemplo abaixo:

~> gcc -c hello.c escreva.c
 
~> ls -l
-rw-r--r--  1  prof     50  Set 28 16:13  escreva.c
-rw-r--r--  1  prof    788  Set 28 16:16  escreva.o
-rw-r--r--  1  prof     45  Set 28 16:13  hello.c
-rw-r--r--  1  prof    800  Set 28 16:16  hello.o

Esses arquivos objeto podem então ser ligados, gerando o executável:

~> gcc -o hello *.o

~> ls -l
-rw-r--r--  1  prof     50  Set 28 16:13  escreva.c
-rw-r--r--  1  prof    788  Set 28 16:16  escreva.o
-rwxr-xr-x  1  prof  11724  Set 28 16:25  hello
-rw-r--r--  1  prof     45  Set 28 16:13  hello.c
-rw-r--r--  1  prof    800  Set 28 16:16  hello.o

As opções usuais de execução do compilador GCC podem ser consultadas em sua página de manual, com o comando 'man gcc' ou 'gcc --help' (com dois hífens).

Opções de compilação

O compilador GCC (e os demais compiladores, em geral) aceitam uma grande quantidade de opções na linha de comando, para ativar funcionalidades específicas. As opções mais usuais são:

Todos os programas desta disciplina devem ser compilados com -Wall; a ocorrência de avisos gera perda de pontos na nota.

Uso de bibliotecas

A linguagem C é rica em poder de expressão, mas relativamente pobre em funcionalidades. Para construir aplicações que fazem uso de funcionalidades específicas, como interfaces gráficas, comunicação via rede, fórmulas matemáticas complexas, etc, devem ser usadas bibliotecas. Algumas bibliotecas encapsulam chamadas do sistema operacional, sendo então chamadas de bibliotecas de sistema, enquanto outras provêm funcionalidades no espaço de usuário, como funções matemáticas e interfaces gráficas.

As bibliotecas mais comuns, utilizadas por todas as aplicações e utilitários do sistema, são:

Por default, essas duas bibliotecas são automaticamente incluídas e ligadas em todos os programas. Para compilar um programa que utiliza outras bibliotecas externas, algumas opções devem ser informadas ao compilador. Por exemplo, considere o seguinte programa, que faz o cálculo de Pi através de uma série de Gregory:


#include <stdio.h>
#include <math.h>

#define TERMOS 100

int main ()
{
  int i ;
  double pi = 0 ;
 
  for (i=0; i < TERMOS; i++)
    pi += pow (-1,i) / (2*i+1) ;
  pi *= 4 ;
 
  printf ("O valor aproximado de Pi é: %f\n", pi) ;
  return (0) ;
}

Ao compilar esse programa, obtemos:

~> gcc pi.c -o pi
/tmp/ccCANYTf.o(.text+0x42): In function `main':
: undefined reference to `pow'
collect2: ld returned 1 exit status

Esse erro ocorre porque o ligador não encontrou a função ''pow'' em nenhum dos arquivos fonte ou objeto informados no comando, nem nas bibliotecas padrão. Essa função se encontra na biblioteca matemática ''/usr/lib/libm'', que deve ser informada ao ligador da seguinte forma:

~> gcc pi.c -o pi -lm

A opção -lm indica que o a biblioteca ''libm'' deve ser ligada ao executável. Da mesma forma, -lpthread indica o uso da biblioteca ''libpthread'', e assim por diante. O ligador procurar por arquivos de bibliotecas nos diretórios padrão (''/lib'', ''/usr/lib'', ...). Pode-se informar outros diretórios de bibliotecas ao ligador através da opção -L (detalhada mais adiante neste texto).

Ligação estática e dinâmica

Há duas formas de ligar as bibliotecas a um programa executável:

A ligação dinâmica é feita por default. Para compilar um programa, ligando-o estaticamente à bibliotecas, devemos executar:

~> gcc -static pi.c -o pi -lm

O utilitário ''ldd'' permite verificar de quais bibliotecas dinâmicas um executável depende:

~> ldd pi
    libm.so.6 => /lib/i686/libm.so.6 (0x40028000)          # matemáticza
    libc.so.6 => /lib/i686/libc.so.6 (0x4004b000)          # padrão
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)  # ligador dinâmico

Para encontrar as bibliotecas dinâmicas, são percorridos os diretórios indicados pelo arquivo de configuração ''/etc/ld.so.conf'' e pela variável de ambiente ''LD_LIBRARY_PATH''. O utilitário ''ldconfig'' permite atualizar as informações sobre bibliotecas dinâmicas nos diretórios padrão (ou nos diretórios informados via linha de comando).

Documentação

O sistema UNIX implementa um sistema de documentação on-line simples, mas bastante útil e eficiente, chamado páginas de manual (man pages). As páginas de manual estão estruturadas em sessões:

O acesso às páginas de manual é normalmente efetuado através do comando ''man''. Assim, ''man ls'' apresenta a página de manual do comando ''ls'', enquanto ''man man'' apresenta a página de manual do próprio comando ''man''. Ambientes gráficos normalmente oferecem ferramentas mais versáteis para consulta às páginas de manual.

Além do comando ''man'', outros comandos são úteis para a busca de informações no sistema: