Resumo
Esta aula explica o que é um programa e o que faz um compilador: a máquina só executa operações elementares sobre números na memória, e o código-fonte precisa ser traduzido para código de máquina antes de ser executado. Com um programa de dois arquivos, percorre as três etapas dessa tradução em C++ (pré-processamento, compilação e ligação) e distingue o que ocorre ao compilar do que ocorre ao executar, com um erro de cada tipo. Compara as linguagens compiladas com as interpretadas, resume a história de C++ até os padrões C++23 e C++26 e precisa qual C++ este curso ensina.
Objetivos de aprendizagem
- Explicar, com um esquema, o percurso de um arquivo-fonte até um executável: pré-processamento, compilação e ligação, com o que entra e o que sai de cada etapa.
- Classificar um acontecimento como próprio da compilação, da ligação ou da execução, como um erro de sintaxe, a falta de uma função ou uma divisão por zero.
- Comparar uma linguagem compilada com uma interpretada segundo o momento em que os erros são detectados e o que o programador vê da máquina.
- Situar no tempo os padrões da linguagem e explicar por que o curso usa C++23.
O que é um programa
Um programa é uma sucessão de instruções que uma máquina pode executar para transformar uns dados em uns resultados. Na aula anterior, Do problema ao algoritmo, viu-se que antes do programa está o algoritmo, o método que resolve o problema, escrito para que uma pessoa o siga. O programa é esse mesmo método escrito para uma máquina, que não entende frases nem intenções, mas ordens elementares: copiar um número de um lugar da memória para outro, somar dois números, compará-los, saltar para outra instrução conforme o resultado.
A memória do computador é uma longa fila de casas numeradas, cada uma com um número de 0 a 255 (um byte). Tudo o que a máquina manipula está ali codificado como números: os dados e também as próprias instruções. Se isso é assim, um programa executável é uma sucessão de números que o processador interpreta como ordens, o código de máquina, e depende do processador: os da família x86-64, comuns nos computadores de mesa, e os ARM, comuns nos celulares e nos Mac recentes, não entendem os mesmos números.
Ninguém escreve hoje em código de máquina. Escreve-se em uma linguagem de alto nível, como C++, cujas instruções se parecem com a notação matemática, em um arquivo de texto chamado código-fonte (ou simplesmente fonte). Daí se segue a necessidade de um tradutor que produza, a partir da fonte, o código de máquina equivalente: em C++, o compilador.
Este é um programa completo em C++. Ainda não é preciso entendê-lo: cada uma de suas linhas será explicada na aula 1.5. Basta ver que é texto, que pode ser lido, e que abaixo aparece o que ele escreveu ao ser executado.
// Será explicado linha por linha na aula 1.5.#include <print>int main(){ std::println("Olá, mundo"); std::println("2 + 3 = {}", 2 + 3);}Olá, mundo2 + 3 = 5Para ver o outro extremo da tradução, considere-se uma função que só soma dois inteiros (as funções serão estudadas na unidade 5). Não é um programa completo, porque lhe falta main, o ponto onde a execução começa, mas o compilador pode traduzi-la por si só.
int add(int a, int b){ return a + b;}O programa objdump mostra o código de máquina que o GCC 15 produziu para ela em um computador x86-64 com Linux: em cada linha, a posição da instrução, seus bytes em hexadecimal e seu nome legível.
$ g++ -std=c++23 -O1 -c add.cpp$ objdump -d add.o0000000000000000 <_Z3addii>: 0: f3 0f 1e fa endbr64 4: 8d 04 37 lea (%rdi,%rsi,1),%eax 7: c3 retA soma inteira se reduziu a oito bytes. A primeira instrução, endbr64, é uma marca de segurança que não calcula nada; a segunda, lea, soma os dois números que a função recebeu em dois registradores do processador (%rdi e %rsi) e deixa o resultado em um terceiro (%eax); a terceira, ret, volta ao ponto de onde a função foi chamada. O nome _Z3addii é a forma como o compilador codifica “função add com dois parâmetros int”.
Duas observações decorrem do exemplo. Primeira: o código de máquina não conserva os nomes a e b, nem a indentação, nem os comentários; tudo isso existia para quem lê a fonte, e o compilador o usou para decidir que instruções escrever. Segunda: o que o programador de C++ escreve se traduz em poucas instruções e sem intermediários; essa proximidade da máquina é a razão da eficiência de C++ e, como se verá no curso, também de alguns de seus perigos.
Carregando o conteúdo…