Exercícios
-
Olá mundo!:
- Escolha um directório de trabalho e entre dentro desse directório. Por exemplo, pode criar na sua área de trabalho um novo directório
IAED_lab1
, escrevendo no terminal (a seguir à prompt$)
o comando$ mkdir ~/IAED_lab1
e entrar no directório criado com o comando:$ cd ~/IAED_lab1
- Utilizando um editor de texto à sua escolha (ex: gedit, emacs, vi, kate, sublime, etc.), crie um ficheiro
hello.c
com o seguinte código, e guarde-o no directório criado:#include <stdio.h>
int main()
{
printf("Olá mundo!\n");
return 0;
} - Experimente compilar e correr, escrevendo no terminal (no mesmo ponto da árvore de directórios em que criou o ficheiro hello.c):
$ gcc
-ansi -pedantic -Wall ola
.c
(A utilização das opções "-ansi -pedantic -Wall
" é sempre recomendada no âmbito desta cadeira.)
Foi criado um ficheiro executávela.out
com o programa que escreveu. - Encontre o ficheiro criado listando o directório corrente com o comando:
$ ls -l
- Pode agora executar o programa criado:
$ ./a.out
- O executável pode ser nomeado durante a compilação com a opção
-o
:$ gcc
-ansi -pedantic -Wall
-o ola ola.c
$ ./ola
- Escolha um directório de trabalho e entre dentro desse directório. Por exemplo, pode criar na sua área de trabalho um novo directório
- Compilação e ligação de múltiplos ficheiros:
- Faça o download do ficheiro
fact.tgz
para o seu directório de trabalho. - Descomprima o ficheiro com o comando:
$ tar xvfz fact.tgz
Foi criado um directório denominadofact
com o conteúdo do arquivo. Entre dentro desse directório.
- Visualize os ficheiros
main.c,
fact.h
,iter.c
erecurs.c
com um editor de texto, e observe:
- declaração da função main: argumentos e tipo de retorno
- valores de retorno
- função do programa
- includes e variáveis globais
- Compile os módulos fonte do programa iterativo (
main.c
eiter.c
) e faça a ligação do código objecto.$ gcc -ansi -pedantic -Wall -o iter main.c iter.c
- Execute o programa
iter
e verifique que este imprime o factorial de 5. - Repita os passos 4 e 5 utilizando a versão recursiva.
- Faça o download do ficheiro
- Passos intermédios do processo de compilação (facultativo):
- Pré-processamento: fase que antecede a compilação e que executa as directivas iniciadas por #. Por exemplo, no ficheiro
main.c
serão processadas as directivasinclude
edefine
.$ gcc -E main.c
O resultado do pré-processamento é enviado para o terminal. (O pré processador pode ser invocado separadamente com o comandocpp
) - Compilação: fase que gera código final em assembly. O assembly ainda tem um formato textual, pode ser lido e modificado por um vulgar editor de texto, mas o código gerado já depende do processador, arquitectura e sistema operativo.
$ gcc -S iter.c
Verifique o código assembly gerado no ficheiroiter.s
. Compare as variantes do ficheiroiter.s
quando utiliza o optimizador (adicionar a opção-O
) e a informação para o debugger (adicionar a opção-g
). - Montagem ou assemblagem: fase que produz os códigos binários, ficheiros objecto (".o", nada tem a ver com linguagens orientada para objectos), que serão processados pelo CPU. Esta fase é independente da linguagem de alto nível utilizada: C, Pascal, Fortran, etc.
$ gcc -c main.c
Verifique o tipo de ficheiro gerado, com o comando:$ file main.o
e as dimensões das secções, com o comando:$ size main.o
e a tabela de símbolos, com o comando:$ nm main.o
- Ligação ou Linkagem: fase que produz o ficheiro executável final através da interligação dos vários ficheiros objectos ou de bibliotecas (conjuntos de ficheiros objecto).
$ gcc -o factorial main.o iter.s
Verifique o tipo de ficheiro gerado e as dimensões das secções. Use o comandoldd
para verificar as dependências das bibliotecas dinâmicas. Tente gerar um ficheiro executável com as variantes iterativa e recursiva, simultaneamente,$ gcc main.c iter.c recurs.c
e verifique que existem duas realizações de factorial com o mesmo nome. Por outro lado, se tentar criar um ficheiro executável apenas com o ficheiromain.c
,$ gcc main.c
falta um ficheiro ou biblioteca que forneça uma realização de factorial. - O processo completo (executável estático vs dinâmico): o comando
gcc
permite, como já pode observar, controlar todo o processo de compilação para a linguagem C. Contudo, pode verificar a execução das diversas fases através da opção-v
.$ gcc -v -static main.c iter.c
Neste exemplo, geramos um executável estático, isto é, não depende na execução da existência das bibliotecas dinâmicas. Como contrapartida, o executável final fica muito maior, pois inclui no próprio ficheiro uma cópia de todas as funções utilizadas, como por exemplo oprintf
. Verifique o tipo, dimensões do ficheiro (ls -l
), dimensões das secções e dependências do ficheiroa.out
gerado, face ao executável dinâmico gerado na alínea anterior. Retire a informação simbólica, com o comandostrip
, e verifique que o executável ficou mais pequeno e que já não é possível saber a posição dos símbolos (comandonm
). - Faça o download do ficheiro fact-makefile.zip. Inspeccione o exemplo de ficheiro "Makefile" com um editor de texto e crie os dois executáveis através do comando make.
- Pré-processamento: fase que antecede a compilação e que executa as directivas iniciadas por #. Por exemplo, no ficheiro
- Criação de projecto usando o Eclipse (Eclipse IDE for C/C++ Developers — procurar por "eclipse-cdt" no software center do ubuntu) (facultativo)
- Crie um projecto de nome factorial (File->New->Project->C Project) e dê um nome ao projecto. Crie uma directoria “src” se esta não existir e um novo source file (.c) nessa directoria.
- Implemente um dos programas discutidos nas aulas teóricas 1 e 2 (ver slides).
- Altere as opções de compilação no Eclipse por forma a incluir as opções
-ansi -pedantic -Wall
(ver “Properties” do projecto). Compile e verifique as mensagens de compilação. Corra o programa e verifique o output na "Console" do eclipse. - Crie um novo projecto e implemente um programa que calcule o valor do factorial de 5 usando uma implementação iterativa com 3 ficheiros, tal como no exemplo anterior.
- Compile e execute o programa. Verifique as mensagens de compilação.
- Inspeccione a estrutura do projecto “factorial” através da linha de comandos. Recompile a versão “Debug” na linha de comandos usando o Makefile criado automaticamente pelo Eclipse (se necessário, faça "make clean", seguido de "make") e corra o programa na linha de comandos.
- Altere o programa de forma a que o valor a calcular seja inserido pelo utilizador.
- Depuração de um programa usando o Eclipse (facultativo)
- Defina um ponto de quebra (breakpoint) no seu programa no início da função que calcula o valor do factorial (double click na barra lateral ou Ctrl+Shift+b na linha desejada).
- Recorrendo à perspectiva “Debug”, execute o programa passo a passo a partir do breakpoint. Verifique o valor das variáveis a cada passo da execução do programa (F5=next step).