Exercícios

  1. Download dos ficheiros necessários:
    1. Faça o download do ficheiro fact.tgz.
    2. Descomprima o ficheiro com o comando: 
      $ tar xvfz fact.tgz
      Foi criado um directório "fact" com o conteúdo do ficheiro.
    3. Entre dentro desse directório com o comando: 
      $ cd fact
    4. Liste os ficheiros com o comando: 
      $ ls -l
       
  2. Compilação:
    1. Visualize o ficheiro main.c com um editor de texto à sua escolha (ex: geditemacsvikate, sublime, etc.)
      • declaração da função main: argumentos e tipo de retorno.
      • valores de retorno.
      • função do programa
      • includes e globais
      • ver fact.hiter.c recurs.c
    2. Compile os módulos fonte do programa iterativo e faça a ligação do código objecto resultante para produzir o executável. 
      $ gcc -ansi -pedantic -Wall -o iter main.c iter.c
    3. Execute o programa e verifique que este imprime o factorial de 5.
       
  3. Passos intermédios do processo de compilação (facultativo):
    1. Pré-processamento: fase que antecede a compilação e que executa as directivas iniciadas por #. Por exemplo, no ficheiro main.cserão processadas as directivas includedefine
      $ gcc -E main.c
      O resultado do pré-processamento é enviado para o terminal. (O pré processador pode ser invocado separadamente com o comando cpp)
    2. 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 ficheiro iter.s. Compare as variantes do ficheiro iter.squando utiliza o optimizador (adicionar a opção -O) e a informação para o debugger (adicionar a opção -g).
    3. 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
      (O assemblador pode ser invocado separadamente com o comando as)
    4. 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 comando ldd  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 ficheiro main.c
      $ gcc main.c
      falta um ficheiro ou biblioteca que forneça uma realização de factorial.
    5. 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 o printf. Verifique o tipo, dimensões do ficheiro ( ls -l), dimensões das secções e dependências do ficheiro a.outgerado, face ao executável dinâmico gerado na alínea anterior. Retire a informação simbólica, com o comando strip, e verifique que o executável ficou mais pequeno e que já não é possível saber a posição dos símbolos (comando nm).
    6. 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.
       
  4. Criação de projecto usando o Eclipse (Eclipse IDE for C/C++ Developers — procurar por "eclipse-cdt" no software center do ubuntu) (facultativo)
    1. 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. 
    2. Implemente um dos programas discutidos nas aulas teóricas 1 e 2 (ver slides). 
    3. 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. 
    4. 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.
    5. Compile e execute o programa. Verifique as mensagens de compilação.
    6. 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.
    7. Altere o programa de forma a que o valor a calcular seja inserido pelo utilizador.
       
  5. Depuração de um programa usando o Eclipse (facultativo) 
    1. 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).
    2. 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).