C++ Compilar com g++

Na aula anterior você compilou com o comando mais curto possível, g++ ola.cpp -o ola. Ele funciona, mas deixa o compilador no "modo de fábrica": usando uma versão antiga do C++ e calado sobre muitos erros prováveis. Nesta aula você monta, peça por peça, o comando que o guia usa em todas as aulas daqui para a frente, e aprende a compilar e rodar numa linha só.

Você vai aprender
  • O comando padrão do guia: g++ -std=c++23 -Wall -Wextra ola.cpp -o ola.
  • O que fazem -std, -Wall, -Wextra e -o, e para que servem -g e -O2.
  • Por que o std::println dá erro sem -std=c++23.
  • Compilar e rodar numa linha com &&.
  • Por que é preciso recompilar depois de editar o código.
  • Como usar o clang++, o outro grande compilador, no lugar do g++.

Todos os comandos foram executados na máquina da Ana com o g++ 15.2 e o clang++ 21.1 do Ubuntu 26.04, na pasta ~/cpp, com o ola.cpp da aula Olá, Mundo.


A ideia: os botões do forno

Voltando à cozinha da aula anterior: o compilador é a cozinheira, e o código é a receita. Mas a cozinheira também precisa saber como trabalhar. "Use a receita nova, não a da vovó." "Me avise se algum ingrediente parecer estragado." "Capriche, que é para festa." Essas instruções extras são as opções do compilador: palavras que começam com - (hífen) e mudam o jeito como ele trabalha, sem mudar o código.


1. O comando padrão do guia

Este é o comando que você vai digitar em quase todas as aulas:

g++
O compilador de C++ do projeto GNU.
-std=c++23
"Use o C++23", a versão da linguagem que o guia ensina. Seção 2.
-Wall
"Avise sobre tudo o que é suspeito" (Warnings: all). Seção 3.
-Wextra
"E mais alguns avisos extras." Seção 3.
ola.cpp
O arquivo de código.
-o ola
O nome do programa que será criado. Seção 4.
$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola
$ ./ola
Olá, mundo!

O resultado é o mesmo de antes, porque o ola.cpp é simples e não tem nada de errado. A diferença aparece quando o código usa recursos novos ou tem problemas escondidos, como você vai ver a seguir.

A ordem não importa (quase)

As opções podem vir em qualquer ordem: g++ ola.cpp -Wextra -o ola -std=c++23 -Wall funciona igual. A única regra é que o nome do programa vem logo depois do -o. O guia usa sempre a mesma ordem, para você se acostumar.


2. -std=c++23: qual C++ usar

O C++ ganha uma versão nova a cada três anos: C++11, C++14, C++17, C++20, C++23 (veja Versões do C++). Cada uma traz recursos novos. O -std (de standard, "padrão") diz ao compilador qual versão seguir.

E se você não disser? O g++ 15 usa o C++17, de 2017. Dá para conferir: todo compilador define um número especial, __cplusplus, com o ano e o mês da versão em uso. Este programa só mostra esse número:

#include <iostream>

int main() {
    std::cout << __cplusplus << "\n";
}
$ g++ versao.cpp -o versao && ./versao
201703                   « sem -std: C++17 (2017, mês 03)
$ g++ -std=c++20 versao.cpp -o versao && ./versao
202002                   « C++20
$ g++ -std=c++23 versao.cpp -o versao && ./versao
202302                   « C++23: o que o guia usa
$ g++ -std=c++26 versao.cpp -o versao && ./versao
202400                   « C++26: ainda em acabamento, por isso o número "provisório"

(O && entre os dois comandos é explicado na seção 5.)

O que acontece sem -std=c++23

O C++23 trouxe o std::println, um jeito mais simples de mostrar texto (aula std::format e std::print). Veja o que acontece ao compilar um programa com ele sem pedir o C++23:

#include <print>

int main() {
    std::println("Olá, {}!", "Ana");
}
$ g++ println.cpp -o println
println.cpp: In function ‘int main()’:
println.cpp:4:10: error: ‘println’ is not a member of ‘std’
    4 |     std::println("Olá, {}!", "Ana");
      |          ^~~~~~~
println.cpp:4:10: note: ‘std::println’ is only available from C++23 onwards
$ g++ -std=c++23 println.cpp -o println && ./println
Olá, Ana!

A primeira mensagem diz "println não é membro de std": o compilador, no modo C++17, não conhece esse nome. A note (nota) logo abaixo é a dica preciosa: "std::println só está disponível a partir do C++23". Com -std=c++23, o mesmo arquivo compila e roda.

c++23 ou gnu++23?

Você pode encontrar -std=gnu++23. É o C++23 mais algumas extensões próprias do GNU, que não existem nos outros compiladores. Na verdade, o padrão de fábrica do g++ 15 é o gnu++17. O guia usa c++23 (sem extensões) para o seu código funcionar em qualquer compilador.

O editor do guia já compila com -std=c++23. Experimente ver o número da versão e o std::println funcionando:

Missão: rode e confira o número da versão. Depois acrescente, antes da }, uma linha com std::println("Olá, {}!", "Ana"); e rode de novo. (O #include <print> já está lá.)

#include <iostream>
#include <print>

int main() {
    std::cout << __cplusplus << "\n";
}
#include <iostream>
#include <print>

int main() {
    std::cout << __cplusplus << "\n";
    std::println("Olá, {}!", "Ana");
}

3. -Wall e -Wextra: peça para ser avisado

O compilador encontra dois tipos de problema:

  • Erros (error:): o código não segue as regras do C++. O programa não é criado.
  • Avisos (warning:): o código segue as regras, mas tem cara de engano. O programa é criado, e o compilador só comenta: "tem certeza?".

No modo de fábrica, o g++ mostra pouquíssimos avisos. As opções que começam com -W (de warning, "aviso") ligam grupos deles. Veja este programa, em que a Ana criou uma variável itens e esqueceu de usá-la (variáveis são o assunto de Variáveis; aqui basta saber que int itens = 3; guarda o número 3 com o nome itens):

#include <iostream>

int main() {
    int itens = 3;
    std::cout << "Lista de compras da Ana\n";
}
$ g++ compras.cpp -o compras
$                        « nenhuma palavra
$ g++ -Wall compras.cpp -o compras
compras.cpp: In function ‘int main()’:
compras.cpp:4:9: warning: unused variable ‘itens’ [-Wunused-variable]
    4 |     int itens = 3;
      |         ^~~~~

Com -Wall, o g++ avisou: "variável não usada: itens". Não é um erro (o programa foi criado), mas é uma pista: ou a variável sobrou, ou você esqueceu de mostrá-la. Entre colchetes, [-Wunused-variable] é o nome do aviso, que você pode pesquisar.

Apesar do nome, -Wall ("todos") não liga todos os avisos. O -Wextra acrescenta outros. Um exemplo: uma main que declara que recebe os argumentos da linha de comando (as palavras digitadas depois do nome do programa; os parâmetros são explicados em Parâmetros e Argumentos) mas não usa nenhum:

#include <iostream>

int main(int argc, char* argv[]) {
    std::cout << "Olá!\n";
}
$ g++ -Wall args.cpp -o args
$                        « o -Wall sozinho não viu nada
$ g++ -Wall -Wextra args.cpp -o args
args.cpp: In function ‘int main(int, char**)’:
args.cpp:3:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
    3 | int main(int argc, char* argv[]) {
      |          ~~~~^~~~
args.cpp:3:26: warning: unused parameter ‘argv’ [-Wunused-parameter]
    3 | int main(int argc, char* argv[]) {
      |                    ~~~~~~^~~~~~

Os avisos têm aula própria, com os casos que realmente salvam programas (comparações que dão resultado errado, variáveis sem valor, = no lugar de ==): Avisos (-Wall -Wextra). Por enquanto, a regra é simples: compile sempre com -Wall -Wextra, e trate cada aviso como um erro a corrigir.

Maiúsculas importam nas opções

-Wall tem W maiúsculo. Com minúsculo, o g++ não reconhece a opção, mas pelo menos sugere a certa:

$ g++ -std=c++23 -wall ola.cpp -o ola
g++: error: unrecognized command-line option ‘-wall’; did you mean ‘-Wall’?
$ g++ -std=c+23 ola.cpp -o ola
g++: error: unrecognized command-line option ‘-std=c+23’; did you mean ‘-std=c++23’?

"Opção de linha de comando não reconhecida; você quis dizer…?" Leia a sugestão e corrija.


4. -o: o nome do programa

Você já conhece: -o ola cria o programa ola; sem -o, o nome é a.out (aula Olá, Mundo). Dois detalhes:

  • Se já existir um programa com esse nome, ele é substituído sem perguntar. É o que você quer quando recompila.
  • O nome depois de -o nunca deve ser o do arquivo .cpp. O g++ 15 recusa (input file ‘ola.cpp’ is the same as output file), mas não conte com isso para outros arquivos.

5. Compilar e rodar numa linha: &&

Você vai compilar e rodar centenas de vezes. Para não digitar dois comandos, junte-os com && ("e então"):

$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola && ./ola
Olá, mundo!

O && é do terminal, não do C++: ele roda o segundo comando só se o primeiro der certo. Se a compilação falhar, o programa não roda. Isso é exatamente o que você quer, como mostra a próxima seção.

Repetir o último comando

No terminal, a seta para cima traz de volta o último comando. O ciclo de trabalho vira: editar, salvar, ir ao terminal, ↑, Enter.


6. Mudou o código? Compile de novo

Lembra do bolo? Se você muda a receita, o bolo que já está pronto não muda sozinho. O programa ola é uma "foto" do ola.cpp no momento em que foi compilado. Veja: a Ana troca mundo por Ana no ola.cpp, salva, e roda o programa sem compilar:

#include <iostream>

int main() {
    std::cout << "Olá, Ana!\n";
}
$ ./ola
Olá, mundo!              « o programa antigo! ninguém o recompilou
$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola
$ ./ola
Olá, Ana!                « agora sim

É um dos sustos mais comuns: "eu mudei o código e nada mudou!". Antes de procurar o erro, pergunte-se: salvei o arquivo? Recompilei?

Por que o && protege você

Agora a Ana troca para Bruno, mas esquece o ;. Com &&:

$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola && ./ola
ola.cpp: In function ‘int main()’:
ola.cpp:4:33: error: expected ‘;’ before ‘}’ token
    4 |     std::cout << "Olá, Bruno!\n"
      |                                 ^
      |                                 ;
    5 | }
      | ~                                

A compilação falhou, o ./ola não rodou, e a tela mostra só o erro. Agora compare com ; (ponto e vírgula do terminal, que roda o segundo comando sempre):

$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola ; ./ola
ola.cpp: In function ‘int main()’:
ola.cpp:4:33: error: expected ‘;’ before ‘}’ token
    4 |     std::cout << "Olá, Bruno!\n"
      |                                 ^
      |                                 ;
    5 | }
      | ~
Olá, Ana!                « o programa ANTIGO rodou, e parece que deu tudo certo

Quando a compilação falha, o g++ não mexe no programa que já existia. Com ;, o terminal roda esse programa velho, e a última linha da tela engana: parece que funcionou. Use sempre &&.


7. Duas opções para depois: -g e -O2

Você não vai precisar delas agora, mas vai encontrá-las em todo lugar.

-g: informações para depurar

O -g guarda dentro do programa um "mapa" que liga cada pedaço do programa à linha do código-fonte. Ele não muda o que o programa faz; só permite que um depurador como o gdb mostre "parei na linha 4" e o valor das variáveis (aula Depurar com o GDB). O preço é um arquivo maior:

-O2: otimizar

-O (letra O maiúscula, de optimize) seguido de um nível: -O0 (nenhuma otimização, o padrão), -O1, -O2, -O3. Com -O2, o compilador gasta mais tempo procurando jeitos de o programa rodar mais rápido: reorganiza contas, elimina trabalho repetido. É o nível usado na maioria dos programas que vão para os usuários.

$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola
$ g++ -std=c++23 -Wall -Wextra -g ola.cpp -o ola-debug
$ g++ -std=c++23 -Wall -Wextra -O2 ola.cpp -o ola-rapido
$ ls -l ola ola-debug ola-rapido
-rwxrwxr-x 1 ana ana 16120 out 16 21:41 ola
-rwxrwxr-x 1 ana ana 50080 out 16 21:41 ola-debug      « o -g triplicou o tamanho: é o "mapa"
-rwxrwxr-x 1 ana ana 16144 out 16 21:41 ola-rapido

Num Olá, mundo não há o que acelerar. Num programa que faz muita conta, a diferença aparece. Este faz 300 milhões de somas (não se preocupe com o for, que é da aula for; o time do terminal mede quanto tempo o programa levou):

#include <iostream>

int main() {
    long long soma = 0;
    for (long long i = 1; i <= 300000000; i++) {
        soma = soma + i % 7;
    }
    std::cout << "Soma: " << soma << "\n";
}
$ g++ -std=c++23 -Wall -Wextra soma.cpp -o soma
$ time ./soma
Soma: 900000003

real	0m1,079s                « sem otimização: pouco mais de 1 segundo
user	0m1,078s
sys	0m0,001s
$ g++ -std=c++23 -Wall -Wextra -O2 soma.cpp -o soma
$ time ./soma
Soma: 900000003

real	0m0,493s                « com -O2: menos da metade do tempo, mesmo resultado
user	0m0,490s
sys	0m0,002s

Enquanto aprende, compile sem -O2 e, para depurar, com -g: o programa otimizado fica tão reorganizado que o depurador não consegue mais seguir as linhas. O -O2 tem um bônus: ele faz o g++ analisar o código mais a fundo e, por isso, achar alguns avisos a mais (aula Avisos).


8. A alternativa: clang++

O g++ não é o único compilador de C++. O outro grande é o Clang (comando clang++), do projeto LLVM, usado pela Apple no macOS e por muitas empresas. Ele aceita as mesmas opções:

$ clang++ --version
Ubuntu clang version 21.1.8 (6ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-21/bin
$ clang++ -std=c++23 -Wall -Wextra ola.cpp -o ola && ./ola
Olá, mundo!

No Ubuntu, ele vem no pacote clang (Instalar no Linux). As mensagens de erro dizem a mesma coisa que as do g++, com outras palavras. Veja o std::println sem -std=c++23 (o Clang 21 também usa C++17 se você não pedir outro):

$ clang++ println.cpp -o println
println.cpp:4:10: error: no member named 'println' in namespace 'std'
    4 |     std::println("Olá, {}!", "Ana");
      |          ^~~~~~~
1 error generated.
$ clang++ -std=c++23 println.cpp -o println && ./println
Olá, Ana!

"Nenhum membro chamado println no namespace std": o mesmo erro do g++, sem a nota sobre o C++23. Saber ler os dois é útil, porque o editor do guia usa o Clang (compilado para rodar no navegador), enquanto os exemplos de terminal usam o g++. Comparações lado a lado dos dois estão em Lendo Erros do Compilador.

g++ (GCC)clang++ (LLVM)
Quem fazProjeto GNUProjeto LLVM
Onde é o padrãoLinuxmacOS (e o editor deste guia)
OpçõesAs mesmas para o dia a dia: -std=c++23 -Wall -Wextra -o -g -O2
MensagensAspas tipográficas ‘assim’, notas longasAspas simples 'assim', terminam com "N errors generated."

Qual usar? Qualquer um. O guia mostra o g++ porque é o que vem no Ubuntu. Compilar o mesmo código com os dois é um bom teste: se os dois aceitam sem avisos, o seu código provavelmente segue o padrão à risca.


Quando dá errado

error: ‘println’ is not a member of ‘std’

O que significa: você usou um recurso de uma versão mais nova do C++ sem o -std correspondente. A note logo abaixo costuma dizer a versão (only available from C++23 onwards). Também acontece quando falta o #include (aqui, #include <print>).

Como consertar: compile com -std=c++23 e confira o #include.

g++: error: unrecognized command-line option

O que significa: uma opção foi digitada errado: -wall, -std=c+23, -Wextras. O g++ costuma sugerir a certa (did you mean …?).

Como consertar: copie a sugestão. Maiúsculas e minúsculas importam.

"Mudei o código e o programa continua igual"

O que significa: você está rodando o programa antigo. Ou o arquivo não foi salvo, ou não foi recompilado, ou a compilação falhou e você rodou o programa velho (com ; em vez de &&, ou rodando ./ola sem ler o erro).

Como consertar: salve, compile com g++ … && ./ola e leia tudo o que aparecer antes da saída do programa.

Apareceram avisos e o programa rodou mesmo assim

O que significa: avisos não impedem a criação do programa. Mas eles quase sempre apontam um engano real.

Como consertar: leia cada warning: e corrija o código até a compilação ficar em silêncio. A aula Avisos mostra os mais comuns.


Referência rápida

OpçãoO que fazQuando usar
-std=c++23Segue o C++23 (sem ela, o g++ 15 usa C++17)Sempre, neste guia
-WallLiga os avisos mais importantesSempre
-WextraLiga avisos extrasSempre
-o nomeNome do programa criado (sem ela: a.out)Sempre
-gGuarda informações para o depuradorAo depurar (GDB)
-O2Otimiza: programa mais rápido, compilação mais lentaNa versão final; para achar mais avisos
-WerrorTransforma avisos em errosProjetos sérios (Avisos)
cmd1 && cmd2Roda cmd2 só se cmd1 deu certoCompilar e rodar: g++ … && ./ola
clang++O compilador Clang, com as mesmas opçõesAlternativa ao g++

Outras opções que você vai conhecer mais adiante: -c e vários arquivos (Compilar Vários Arquivos), -fsanitize (Sanitizers), -I e -l (Usar Bibliotecas Externas). Quando os comandos ficarem compridos, ferramentas como Make e CMake digitam por você.


Exercícios

1. A versão

Complete o comando para compilar seguindo o C++23:

g++ -Wall -Wextra jogo.cpp -o jogo

2. Compilar e rodar

Complete para rodar o ./jogo só se a compilação der certo:

g++ -std=c++23 -Wall -Wextra jogo.cpp -o jogo ./jogo

3. Depurar

Qual opção guarda no programa as informações que o gdb usa?

g++ -std=c++23 -Wall -Wextra jogo.cpp -o jogo

4. Você editou e salvou o jogo.cpp, rodou ./jogo e viu a saída antiga. Por quê?

5. Sem nenhum -std, qual versão do C++ o g++ 15 segue?

6. O que acontece com um programa que gerou só avisos (warning:), sem erros?

Missão final: o recado em C++23

Missão: o editor compila como o comando padrão do guia (-std=c++23 -Wall -Wextra). Este programa compila, mas com um aviso. Faça três coisas: (1) rode e leia o aviso; (2) apague a linha que o causa; (3) troque o std::cout por std::println, que já quebra a linha sozinho (por isso, sem o \n), e acrescente uma segunda linha: 1 kg de arroz.

#include <iostream>
#include <print>

int main() {
    int itens = 3;
    std::cout << "Lista de compras da Ana\n";
}
#include <iostream>
#include <print>

int main() {
    std::println("Lista de compras da Ana");
    std::println("1 kg de arroz");
}

Resumo

  • Comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.
  • -std=c++23 escolhe a versão; sem ele, o g++ 15 usa C++17 e não conhece o std::println.
  • -Wall -Wextra ligam os avisos: o programa é criado, mas o compilador aponta enganos prováveis.
  • -g serve ao depurador; -O2 deixa o programa mais rápido.
  • g++ … && ./programa compila e roda, e não roda nada se a compilação falhar.
  • Mudou o código? Salve e recompile. O programa antigo não muda sozinho.
  • clang++ aceita as mesmas opções; o editor do guia usa o Clang.