MC102MN

Introdução a Algoritmos e programação de computadores

Aula II, apresentação a linguagens de programação

Na aula passada, vimos um modelo simples para entender como computadores funcionam e como podemos programá-los. Na aula de hoje vamos chegar um passo mais próximo de programas de verdade, e estudar linguagens de programação e tipos de dados, bem por cima.

Linguagens de programação

A partir do modelo da aula passada já podemos ter uma idéia de como se passa um programa para o computador executar. Primeiro, cria-se uma sequência de comandos (e dados) em endereços de memória fixos (para que a parte de recalcular M funcione), copiamos essas instruções para a memória do computador e mandamos o processador executá-las (por exemplo, ligando o processador). Isso é surpreendentemente parecido, dado a sua simplicidade, de como um computador funciona na vida real. No entanto, é óbvio que escrever um programa num modelo desses é bem difícil, pelos seguintes (dentre outros) motivos:

  • ao colocar uma instrução no meio, você teria que atualizar todos os comandos que usam explicitamente o valor de M, já que a posição de várias instruções mudou
  • ao adaptar o programa para um computador diferente você teria que reescrevê-lo por inteiro, já que muito provavelmente o tipo de instrução e o modelo de memória dos computadores serão diferentes
  • ao fazer contas complexas com vários números seria muito fácil se perder, já que a única identidade que uma variável pode ter é um número de posição de memória que não tem nada a ver com o que ela significa para você
  • fazer a mesma coisa em dois pontos diferentes do programa provavelmente implicaria em ter o mesmo código duplicado (o que tornaria difícil para você mesmo ou outra pessoa saber o que fazer quando perceber que existe um bug naquele cálculo)
  • ao manipular um número, você teria que se lembrar se ele é um número inteiro ou um número real (já que os dois normalmente são processados de forma completamente diferente por um computador).

Para resolver esse tipo de problema, foram criadas linguagens de programação, que são formas mais inteligíveis (para pessoas) de especificarem as instruções e os dados que estão na memória de um computador mas que um computador consegue executar sem muita perda de performance.

Pra clareza de comunicação, todas as formas de se escrever um programa pra um computador executar são chamadas de linguagens de programação. Métodos de escrever programas que consistem imediatamente ou quase imediatamente em escrever código de máquina são chamados de linguagens de baixo nível. Esse é um programa em linguagem de baixo nível para calcular a média de vários valores:

inicio: add eax,[esi+ecx]
        cmp ecx,1
        jle inicio
        div eax

Já linguagens que criam mais camadas de abstração entre o código executado pelo computador e o escrito pelo programador são chamadas linguagens de alto nível. Esse é um programa em uma linguagem de muito alto nível para calcular a média de vários valores:

def media(valores):
   return sum(valores)/len(valores)

Nesse curso trabalharemos com uma linguagem de alto nível, mas não muito alto nível, chamada C. Um programa C para calcular a média de vários valores pode ser:

double media(double[10] valores) {
  int i, m = 0;
  for (i = 0; i < 10; i = i+1) {
    m = m + valores[i]
  }
  return m/10
}

Ao longo do curso, esse programa fará mais e mais sentido para vocês.

Interpretação vs compilação

Existem duas famílias de estratégias para executar programas escritos em linguagens de programação em computadores. A primeira, interpretação, é quando existe um programa já escrito, chamado interpretador, que lê arquivos texto com o código-fonte e executa as instruções. Quase todas as linguagens de pacotes estatísticos são interpretadas.

A outra estratégia, compilação, é quando um programa é usado para traduzir o código fonte em código de máquina. C é uma linguagem compilada. Isso significa que para executar um programa C, o caminho é algo como:

  • editar o programa em um arquivo de texto puro (não word nem nada parecido)
  • salvar como algumacoisa.c
  • executar o compilador para gerar um arquivo executável
  • executar o arquivo executável

Esse arquivo executável pode ser executado em outras máquinas (com o mesmo sistema operacional e arquitetura) sem problemas, e sem a necessidade do compilador.

A linguagem C

A linguagem C de programação foi criada na década de 70 por cientistas do bell labs para ser usada na programação do sistema operacional UNIX.

  • Estrutura de um programa C

    Vamos olhar para o programa exemplo e destrinchá-lo por partes,

    #include <stdio.h>
    
    int main(int argc, char *argv[]) {
      printf("Bem vindo a MC102.\n");
      return 0;
    }
    
    

    O programa pode ser dividido em duas partes.

    A primeira, o cabeçalho, nesse caso só tem a linha

    #include <stdio.h>
    
    que diz que o programa quer usar as funções de entrada e saída da biblioteca padrão. Vocês não precisarão se preocupar com o significado dos comandos de include nesse curso.

    O restante do programa é composto de declarações de funções e procedimentos. Uma função (ou procedimento) é um bloco de código que pode ser executado várias vezes de outros pontos do programa. Todo programa em C é estruturado através de funções, e sua execução gira em torno da função main. A função main tem sempre a mesma estrutura.

    Uma declaração de função tem duas partes: a sua especificação dos tipos e o bloco de código relacionado. No caso da função main, nessa disciplina, declararemos ela sempre como

    int main(int argc, char *argv[])
    
    Isso significa que ela retorna um valor inteiro (o primeiro "int") e recebe dois parâmetros, um inteiro chamado "argc" e um ainda não explicado chamado argv. O modelo para declarar funções arbitrárias em C é parecido com isso, e estudaremos mais tarde.

    Um bloco de código em C é uma sequência de comandos, normalmente rodeados por "{" e "}" (mas em alguns casos essas chaves podem ser omitidas). Um comando C pode ser de vários tipos (atribuição, execução de procedimento, etc), mas sempre é terminado com um ";". Assim, as linhas

    printf("Bem vindo a MC102.\n");
    
    e
    return 0;
    
    constituem comandos.

    O comando printf, que será estudado mais tarde, escreve coisas na tela, e e um dos comandos que sera usado nessa aula para fazer saida de dados.

    Toda função é terminada por um comando "return", e a função main é normalmente terminada com o comando "return 0".

Expressões e comandos

Toda linha de código em C é um comando, e alguns comandos são expressões. Um bom jeito de entender comandos e expressões é que comandos fazem coisas acontecer e expressões geram valores que podem ser usados em comandos ou outras expressões.

Até agora vimos três tipos de comando: comando para declarar funções, comando para imprimir coisas na tela e comando para retornar valores de funções. Um outro comando muito importante é o de atribuição

Expressões normalmente são coisas como a+b, a-b, etc, e normalmente manipulam valores que são eventualmente guardados em uma variável ou utilizados em comandos mais complexos. Uma boa coisa para se ter em mente

Um tipo de comando importante em C é a declaração de variável. Esse comando te dá um nome (variável) associado a um lugar na memória, onde você pode guardar um valor. Para utilizar uma variável em C, basta declarar o seu tipo e o seu nome; por exemplo,

int x;
define uma variável inteira chamada x e
double f;
define uma variável real chamada f. Não se esqueçam dos ponto-e-vírgulas no final das declarações! As declarações têm que ser as primeiras coisas dentro de um bloco de código.

Após rodar esses comandos, que devem sempre ser colocados no começo de qualquer bloco, você pode usar o valor guardado nas variáveis correspondentes ou definir novos valores para essas variáveis usando comandos de atribuição, como

x = 10 - 2;

Caso haja dúvidas sobre a ordem de execução de uma expressão, a melhor alternativa é colocar parênteses. O ideal são expressões da forma

(-b + sqrt(b*b - 4*a*c))/(2*a)
que não permitem confusão na hora de interpretá-las.

Na próxima aula aprenderemos mais comandos e expressões com mais calma, e o que eles fazem e como funcionam.

Nomes de variáveis

Nomes de variáveis são palavras que começam com letras e são seguidas por letras, números ou _ (underline). Letras maiúsculas e minúsculas fazem a diferença. Evitem usar letras maiúsculas como nomes de variáveis.

Tipos de dados

Computadores fazem operações com vários tipos diferentes de dados. Internamente, todos esses tipos são representados de forma idêntica (como bits na memória), mas os seus programas devem manipulá-los de formas bem distintas. Para isso, as linguagens de programação tendem a usar o conceito de tipos de dados. Alguns tipos que serão bastante utilizados na linguagem de programação C são:

intum número inteiro de 32 bits
unsigned intum número natural de 32 bits
floatum número real com precisão de 32 bits
doubleum número real com precisão de 64 bits
chartanto pode ser um inteiro pequeno como um caracter de texto
voidé um tipo virtual, nenhum valor é void
(os tamanhos estão especificados pensando na arquitetura intel de 32 bits e na convenção usada pela maioria dos compiladores C)

Esses tipos são básicos no sentido de que quase todos os outros tipos que manipularemos ao longo desse curso podem ser representados como coleções desses tipos. Palavras, frases e arquivos de texto inteiros, por exemplo, podem ser representados como sequências de caracteres. Matrizes, vetores, e números complexos podem ser representados como sequências de valores com precisão simples ou dupla. Endereços de memória podem (mas não são!) ser representados por valores inteiros, e por aí vai.

Um inteiro é escrito, em C, como uma sequência de números, como 2, 4, 1234, etc. Números reais usam . (ponto) como separador (como 12.34 , 656.32222 , 3.14 , etc). Caracteres são escritos dentro de aspas simples, como 'a' ou 'b'.

  • Uma nota sobre precisão finita

    Vocês notaram que eu indiquei explicitamente quantos bits são usados para números reais. Por que isso é importante?

    Um computador pode manipular apenas um número finito de bits para fazer qualquer operação. Para a maior parte dos programas que faremos em sala de aula podemos ignorar o fato de que não podemos armazenar números arbitrariamente grandes (no caso de inteiros) ou precisos (no caso de números reais), mas é muito fácil cair em casos em que se estoura o limite de tamanho permitido para uma variável inteira ou o limite de precisão permitido para uma variável real, e precisa-se tomar cuidado com isso.

    Existe muita pesquisa em como projetar algoritmos que fazem muitas contas e se mantém dentro da precisão permitida pelo computador. Por exemplo, ao trabalhar com probabilidades, é normal utilizar ln(P(x)) em vez de P(x) se você vai fazer muitas multiplicações (que, após calcular o logaritmo, viram somas), para evitar lidar com números muito próximos de zero (que acabam virando zero por causa da precisão finita).

Author: Alexandre Tachard Passos <alexandre.tp@gmail.com>

Date: 2010-08-05 20:44:29 BRT

HTML generated by org-mode 6.21b in emacs 23