Menu

Compilando C++: do código-fonte a um executável com g++

Como o C++ transforma código-fonte .cpp em um executável nativo: compile com g++ (ou clang++/MSVC), rode o binário e leia os erros do compilador quando algo dá errado.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Compile, depois rode

Agora que você tem um compilador instalado, fazer um programa em C++ rodar é um ciclo de duas etapas. Diferente de uma linguagem de script, em que um interpretador lê seu arquivo linha por linha, o C++ primeiro compila seu código-fonte em um executável nativo (um binário independente de código de máquina) e depois você roda esse binário diretamente. Não há nenhum interpretador entre o seu programa e a CPU em tempo de execução.

Essa separação é o motivo de o C++ ser rápido, e também o motivo de um erro de sintaxe te parar em tempo de compilação em vez de no meio de uma execução. O compilador verifica o arquivo inteiro antes de produzir qualquer coisa.

Você pode rodar qualquer exemplo autocontido aqui mesmo na página - o editor abaixo compila e roda para você. Mas vale a pena saber o que acontece na sua própria máquina, porque é lá que os projetos de verdade vivem.

O ciclo de compilar e rodar na sua máquina

Digamos que você salvou isto como main.cpp:

#include <iostream>
using namespace std;

int main() {
    cout << "Hello from the terminal" << endl;
    return 0;
}

Abra um terminal na pasta que contém o arquivo e invoque o compilador. O compilador mais comum é o g++ (parte do GCC); o clang++ funciona de forma idêntica para tudo aqui:

g++ main.cpp -o main

Se o código compila sem problemas, o g++ não imprime nada e cria um arquivo novo ao lado do seu fonte: um executável chamado main (ou main.exe no Windows). Esse binário é código de máquina - não é texto legível, e está atrelado ao seu sistema operacional e à sua CPU. Agora rode-o:

./main
Hello from the terminal

No Windows você o rodaria como main.exe (ou só main) a partir do mesmo terminal. O ./ no macOS e no Linux diz ao shell "o programa está bem aqui, nesta pasta, não em algum lugar do PATH" - um passo que iniciantes esquecem e depois se perguntam por que main diz command not found.

O que o -o faz (e o a.out)

A flag -o dá nome à saída. Se você a omite, o g++ ainda compila, mas grava o executável com um nome padrão: a.out no macOS/Linux, a.exe no Windows.

g++ main.cpp        # produces a.out, not main
./a.out

Isso confunde as pessoas o tempo todo: elas compilam, não veem erros, rodam ./main e recebem No such file or directory - porque o binário na verdade se chama a.out. Sempre passe -o para saber exatamente o que está rodando.

Você compila uma vez a cada mudança no código-fonte. Depois disso, o executável é independente - você pode rodar ./main cem vezes sem recompilar. Você só roda o g++ de novo depois de editar o arquivo .cpp.

Escolhendo um padrão de C++

O C++ tem versões - C++11, C++14, C++17, C++20, C++23 - cada uma adicionando recursos à linguagem. O detalhe: seu compilador escolhe um padrão default que pode ser mais antigo do que você espera, então código moderno pode não compilar sem motivo aparente. Defina o padrão explicitamente com -std:

g++ -std=c++17 main.cpp -o main
g++ -std=c++20 main.cpp -o main

Aqui está um código que usa um recurso do C++17 (structured bindings). Ele compila bem no editor, mas na sua máquina precisa de -std=c++17 ou mais novo:

Se você um dia vir um erro como 'structured bindings' only available with '-std=c++17', a correção não está no seu código - está em adicionar a flag -std certa. Ao longo deste curso, assuma C++17 ou mais novo.

Ligue os avisos

Um programa em C++ pode compilar sem problemas e ainda assim estar errado. O compilador, se você pedir, vai apontar código suspeito antes que ele te morda em tempo de execução. Adicione -Wall -Wextra:

g++ -std=c++17 -Wall -Wextra main.cpp -o main

Considere este programa. Ele compila sem -Wall, mas tem um bug de verdade - ler uma variável que nunca recebeu um valor, o que é comportamento indefinido:

#include <iostream>
using namespace std;

int main() {
    int count;                 // never initialized
    cout << count << endl;     // reads garbage - undefined behavior
    return 0;
}

Com os avisos ligados, o compilador o sinaliza:

warning: 'count' is used uninitialized [-Wuninitialized]

Crie o hábito de compilar com -Wall -Wextra desde o primeiro dia. Os avisos são o compilador fazendo uma revisão de código de graça; ignorá-los é como bugs sutis sobrevivem. A correção aqui é simplesmente int count = 0;.

Lendo os erros do compilador

Quando o g++ rejeita seu código, ele aponta o arquivo, a linha e o que deu errado. Aprender a ler essas mensagens é metade do caminho para se desenrolar. Aqui está o clássico - um ponto e vírgula faltando:

#include <iostream>
using namespace std;

int main() {
    cout << "Oops"      // no semicolon
    return 0;
}
main.cpp:5:5: error: expected ';' before 'return'
    5 |     return 0;
      |     ^~~~~~

Algumas coisas para notar. O erro reporta a linha 5, mas o erro está na linha 4 - o compilador só percebe que falta um ponto e vírgula quando chega ao próximo token. Então, quando um erro aponta para uma linha que parece certa, verifique a linha anterior. main.cpp:5:5 é arquivo, linha e depois coluna. Corrija a única coisa que ele nomeia e recompile - resista a chutar.

Erros do compilador significam que nada rodou ainda. Eles são diferentes dos erros em tempo de execução, em que o programa começou a rodar e depois travou. Pegar erros em tempo de compilação, antes de o programa sequer rodar, é uma das maiores forças do C++.

Um programa de verificação

Rode isto no editor, ou salve como main.cpp e faça g++ -std=c++17 -Wall main.cpp -o main e depois ./main na sua própria máquina. Se as três linhas aparecerem, sua cadeia de ferramentas funciona de ponta a ponta:

Três coisas aparecem aqui que você vai conhecer direito em breve: uma variável int, um vector (o array redimensionável do C++) e o cout para a saída. Por enquanto basta que o programa compile e imprima as três linhas em ordem.

Próximo: Sintaxe de C++

Você compilou e rodou alguns programas, mas passamos por cima da pontuação - as linhas #include, as chaves, os pontos e vírgulas, o int main() e por que cada linha tem a aparência que tem. A próxima página destrincha a sintaxe de C++ peça por peça, para que a estrutura deixe de parecer código padrão e comece a fazer sentido.

Perguntas frequentes

Como eu compilo e rodo um programa em C++?

Salve seu código como main.cpp, abra um terminal nessa pasta e rode g++ main.cpp -o main para gerar um executável. Depois, rode-o com ./main no macOS/Linux ou main.exe no Windows. Você compila uma vez; pode rodar o binário resultante quantas vezes quiser.

O que a flag -o faz no g++?

-o dá nome ao arquivo de saída. g++ main.cpp -o hello cria um executável chamado hello. Sem -o, o g++ usa o nome padrão a.out (ou a.exe no Windows) - por isso iniciantes muitas vezes acabam rodando um arquivo que nem sabiam que tinham criado.

Como compilo C++ com um padrão específico, como C++17 ou C++20?

Passe a flag -std: g++ -std=c++17 main.cpp -o main ou -std=c++20. Sem ela, o compilador usa o próprio padrão, que pode ser mais antigo do que você espera, então recursos mais novos como structured bindings ou <ranges> podem não compilar até você definir o padrão explicitamente.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR