Contribuíram neste tutorial Giovanni Bertão, Guilherme Vieira Leite
Instalando um compilador
Diferentemente dos scripts em Python, um código-fonte em C precisa ser “transformado” em um arquivo binário para que ele possa ser executado pelo computador. Para isso, é necessário passar por algumas etapas:
- traduzir o código fonte em código em uma linguagem de montagem ou assembly;
- montar o código assembly em código objeto binário;
- juntar os códigos objetos e bibliotecas externas em um arquivo binário executável.
Todo esse processo é chamado de compilação e é normalmente realizado por um único programa, o compilador. Na disciplina de MC202, utilizaremos o GCC para compilar código-fonte escrito em C.
Nesse tutorial, você aprenderá a baixar e instalar o GCC e depois a compilar um programa usando o GCC.
GNU/Linux
No Ubuntu ou no Debian, para instalar as principais ferramentas para compilação (incluindo o GCC), digite:
user@desktop:~$ sudo apt update && sudo apt install build-essential -y
Para verificar que o GCC foi instalado, digite gcc --version
. Se
tudo foi instalado como esperado, então uma mensagem irá semelhante a
seguinte:
user@desktop:~$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Mac
Windows
Nesta disciplina e durante sua carreira como computeir@, é
provável que você se depare diversas vezes com alguma distribuição de
GNU/Linux. Quanto mais cedo você conseguir fazer essa transição,
melhor. Caso você não possa ou não queira instalar uma versão nativa
agora, você pode utilizar uma versão WSL do Ubuntu que roda
diretamente no Windows 10 ou superior. Siga os passos abaixo para
instalar e utilizar o gcc
, gdb
.
-
Se ainda não tiver instalado, utilize o tutorial para instalar WSL.
-
Nos passos seguintes, vamos utilizar o VSCode para realizar as tarefas da disciplina. Se ainda não tiver instalado, instale o VSCode seguindo as instruções do tutorial.
-
Antes de iniciar o WSL, você precisa instalar uma extensão no VSCode. Abra o VSCode, selecione o ícone de extensões (ou digite
Ctrl + Shift + X
), busque por WSL e instale a extensão correspondente. -
Depois, vamos atualizar seu terminal GNU/Linux e garantir que ele funcione com todas as ferramentas de que você vai necessitar. Abra o terminal do WSL (se o terminal do WSL já estava aberto enquanto você instalou o VSCode, será necessário fechar e abrir o WSL novamente).
-
Atualize o índice de pacotes do sistema e instale os pacotes necessários para a disciplina:
usuario@PC:/mnt/c/Users/usuario$ sudo apt-get update usuario@PC:/mnt/c/Users/usuario$ sudo apt-get install build-essential gdb valgrind
-
Por padrão o WSL já deve ter instalado o
git
. Faça um clone de seu repositório e mude para a pasta:usuario@PC:/mnt/c/Users/usuario$ git clone https://gitlab.ic.unicamp.br/mc202-2022/raXXXXXX.git usuario@PC:/mnt/c/Users/usuario$ cd raXXXXXX
-
Abra o diretório do repositório usando o VS Code usando o modo WSL. Por exemplo, no terminal Ubuntu, a partir do diretório, digite:
usuario@PC:/mnt/c/Users/usuario/raXXXXXX$ code .
-
Se aparecer
WSL: Ubuntu
no canto inferior esquerdo, deu tudo certo! -
Abra algum
arquivo.c
e clique nesse botãoPlay
para compilar e rodar. Você também pode configurar breakpoints e executar passo a passo. -
Parabéns, agora você está com tudo pronto!
Compilando seu primeiro programa
Para compilar um programa utilizando o GCC utilize o comando:
user@desktop:~$ gcc <nome do programa.c> -o <nome do executavel>
Esse tutorial acompanha o código fonte ola.c
.
#include <stdio.h>
int main(void) {
printf("Olá Mundo!\n");
return 0;
}
Compile esse arquivo utilizando
user@desktop:~$ gcc ola.c -o ola
Em seguida, para executar o programa utilize:
user@desktop:~$ ./ola
É esperado que a saída do programa seja:
Olá Mundo!
Adicionando flags ao compilador
A seção anterior uma forma básica de compilar um único arquivo. O gcc
recebe o nome de um arquivo de código-fonte ola.c
de entrada além de
-o ola
indicando que o nome do arquivo executável de saída será
ola
. Observe que o argumento -o
é uma opção do gcc que necessita
de um argumento, então o nome do executável virá sempre imediatamente
depois de -o
.
Além desses argumentos, iremos adicionar diversos outros parâmetros. Eles servem para modificar o processo de compilação e são normalmente chamados de flags de compilação. Por exemplo, na disciplina sempre usaremos flags que determinam qual versão do padrão C iremos utilizar, bem como flags que indicam quais erros devem ser mostrados:
user@desktop:~$ gcc -std=c99 ola.c -Wall -Werror -o ola
-std=c99
: utiliza o padrão C99.-Wall
: habilita todos os warnings.-Werror
: classifica os warnings como erros.
Para ver porque essas flags são úteis, compile o seguinte programa,
chamado inicializar.c
. Esse programa tem um problema importante: ele
tente imprimir o valor de uma variável antes mesmo de definir seu
valor.
#include <stdio.h>
int main(void) {
int x;
printf("O valor de x é %d\n", x);
x = 10;
return 0;
}
Compile e execute sem nenhuma flag, apenas com a flag -Wall
e, em
seguida, com as flags -Wall
e -Werror
. Qual o comportamento em
cada caso?
Finalmente, sempre incluiremos também a flag -g
que é bastante útil
para debugar nosso programa. Ela anexa ao arquivo binário executável
diversas informações de depuração que podem ser utilizadas, por
exemplo, pelo GDB enquanto debugamos o programa.
Às vezes também precisamos passar flags para a última etapa da
compilação. Por exemplo, o seguinte programa raiz.c
utiliza uma
biblioteca externa chamada math.h
, que nos sistemas Unix é
armazenada em um arquivo chamado m.a
, que está armazenado em um
diretório padrão conhecido pelo GCC.
#include <math.h>
#include <stdio.h>
int main(void) {
double valor, raiz;
scanf("%lf", &valor);
raiz = sqrt(valor);
printf("%lf\n", raiz);
return 0;
}
Se tentarmos compilar, teremos um erro:
user@desktop:~$ gcc -std=c99 -Wall -Werror raiz.c -o raiz
/usr/bin/ld: /tmp/ccHWyK6A.o: na função "main":
raiz.c:(.text+0x3d): referência não definida para "sqrt"
collect2: error: ld returned 1 exit status
Você não precisa entender todos os detalhes. O que aconteceu foi o
seguinte: as duas primeiras fases da compilação (tradução e montagem)
funcionaram sem erros. Tanto é verdade que foi gerado um arquivo de
código objeto (no caso, /tmp/ccHWyK6A.o
). Mas esse programa depende
de uma função sqrt
que está em uma biblioteca externa. Para que
possamos ligar (link) nosso programa com essa biblioteca, precisamos
passar uma flag para o linker (ld
) informando para juntar o nosso
arquivo objeto com a biblioteca externa m.a
. Para isso, precisamos
passar a flag -lm
. Como essa é a última etapa, o GCC exige que essa
flag venha por último. Compilando com todas as devidas flags e
executando, obtemos:
user@desktop:~$ gcc -std=c99 -Wall -Werror raiz.c -o raiz -lm
user@desktop:~$ ./raiz
50
7.071068