C++ Sintaxe
Sintaxe é o conjunto de regras de escrita de uma linguagem: onde vai cada símbolo, o que termina uma frase, o que separa uma parte da outra. Em português, um leitor entende "oi tudo bem" mesmo sem vírgula nem ponto de interrogação. O compilador de C++ não é assim tão tolerante: um ponto e vírgula a menos, uma letra maiúscula no lugar errado, e ele recusa o programa inteiro. Nesta aula você conhece as regras básicas, e também as liberdades que a linguagem dá.
- O que é uma instrução e por que ela termina com
;. - Para que servem as chaves
{ }(os blocos). - Por que
maineMain,couteCoutsão coisas diferentes. - O que o compilador ignora (espaços, linhas em branco, recuo) e o que ele não ignora.
- Por que um texto entre aspas não pode quebrar de linha.
- O que é o
std::, o que faz ousing namespace std;e por que o guia evita usá-lo.
Todos os programas foram compilados na máquina da Ana com o comando padrão do guia, g++ -std=c++23 -Wall -Wextra (aula Compilar com g++), com o g++ 15.2; alguns também com o clang++ 21.1, o compilador que o editor do guia usa.
A ideia: a gramática de uma receita
Uma receita tem uma estrutura: um título, uma lista de passos, cada passo numa frase que termina com ponto. "Bata os ovos. Junte o açúcar. Leve ao forno." Um programa em C++ é parecido:
| Na receita | Em C++ | Exemplo |
|---|---|---|
| Uma frase com uma ordem ("Bata os ovos.") | Uma instrução | std::cout << "Oi\n"; |
| O ponto final | O ponto e vírgula | ; |
| Um grupo de passos com título ("Massa:", "Cobertura:") | Um bloco, entre chaves | int main() { … } |
| As palavras, que precisam estar escritas certo | Os nomes, em que maiúsculas importam | main, std::cout |
1. Instruções e o ponto e vírgula
Uma instrução (em inglês, statement) é uma ordem completa para o computador: "mostre isto", "guarde aquilo", "some estes números". Em C++, quase toda instrução termina com ;. Este programa tem duas:
#include <iostream>
int main() {
std::cout << "Primeira instrução\n";
std::cout << "Segunda instrução\n";
}
O ; é o que diz ao compilador onde uma instrução acaba e a próxima começa. Não é a quebra de linha: para o compilador, o fim da linha não significa nada. Por isso, isto também funciona, tudo numa linha só:
#include <iostream>
int main(){std::cout<<"Uma linha só!\n";std::cout<<"E funciona.\n";}
$ g++ -std=c++23 -Wall -Wextra umalinha.cpp -o umalinha && ./umalinha Uma linha só! E funciona.
Funciona, mas ninguém consegue ler. Escreva uma instrução por linha.
Onde não vai ponto e vírgula
- Linhas que começam com
#, como#include <iostream>. Elas são ordens para o pré-processador, não instruções de C++, e terminam no fim da linha. - Depois da
}que fecha uma função, como a damain. - Depois de
int main(), antes da{. Ali começa a função, não termina uma instrução.
E um ; sobrando? Um ; sozinho é uma "instrução vazia", que não faz nada. Este programa compila sem erro nem aviso:
#include <iostream>
int main() {
std::cout << "Olá!\n";;
;
}
$ g++ -std=c++23 -Wall -Wextra pontos.cpp -o pontos && ./pontos Olá!
Inofensivo aqui, mas não se acostume: mais adiante, um ; a mais logo depois de um if ou de um for muda o sentido do programa (você vai ver nas aulas if e else e for). O ; a menos é que dá erro, e ele está na seção Quando dá errado.
2. Blocos: as chaves { }
As chaves agrupam várias instruções num bloco. Você já conhece um: o corpo da main, que vai da { até a }. Um bloco pode ter outros blocos dentro, e vai ter, quando você aprender if, while e for. Dá até para abrir um bloco "solto":
#include <iostream>
int main() {
std::cout << "Antes do bloco\n";
{
std::cout << "Dentro do bloco\n";
}
std::cout << "Depois do bloco\n";
}
$ g++ -std=c++23 -Wall -Wextra bloco.cpp -o bloco && ./bloco Antes do bloco Dentro do bloco Depois do bloco
Aqui o bloco do meio não muda nada na saída. Ele só começa a fazer diferença com variáveis: o que é criado dentro de um bloco deixa de existir quando o bloco fecha (aula Escopo).
Três regras das chaves:
- Toda
{tem a sua}, como parênteses numa conta. Uma chave sobrando ou faltando é erro (veja Lendo Erros do Compilador). - A última que abriu é a primeira que fecha. No exemplo, a
}da linha 7 fecha o bloco do meio; a da linha 9 fecha amain. - Não vai
;depois da}de um bloco ou de uma função. (Há exceções, como as structs, que você verá no Nível 2.)
3. Maiúsculas e minúsculas fazem diferença
O C++ é sensível a maiúsculas e minúsculas (em inglês, case-sensitive). Para ele, main, Main e MAIN são três nomes diferentes, tão diferentes quanto main e banana. Quase tudo da linguagem e da biblioteca padrão se escreve em minúsculas: int, main, return, std, cout, include.
Veja o que acontece com uma maiúscula no lugar errado em cada parte do Olá, mundo.
Main em vez de main
#include <iostream>
int Main() {
std::cout << "Olá!\n";
return 0;
}
$ g++ -std=c++23 -Wall -Wextra Main.cpp -o Main /usr/bin/x86_64-linux-gnu-ld.bfd: /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/Scrt1.o: na função "_start": (.text+0x1b): referência não definida para "main" collect2: error: ld returned 1 exit status
O compilador não reclamou de nada: Main é um nome válido para uma função qualquer. Quem reclamou foi o ligador (ld), a etapa final, que procura a função main para saber onde o programa começa e não a encontrou: "referência não definida para main" (em inglês, undefined reference to `main'). Esse tipo de erro, que aparece depois da compilação, é explicado em Lendo Erros do Compilador.
Cout em vez de cout
#include <iostream>
int main() {
std::Cout << "Olá!\n";
}
$ g++ -std=c++23 -Wall -Wextra cout.cpp -o cout
cout.cpp: In function ‘int main()’:
cout.cpp:4:10: error: ‘Cout’ is not a member of ‘std’; did you mean ‘cout’?
4 | std::Cout << "Olá!\n";
| ^~~~
| cout
"Cout não é membro de std; você quis dizer cout?" O g++ percebeu que existe um nome muito parecido e sugeriu a correção, escrita embaixo da palavra errada. O Clang (o compilador do editor do guia) diz o mesmo com outras palavras:
$ clang++ -std=c++23 -Wall -Wextra cout.cpp -o cout
cout.cpp:4:10: error: no member named 'Cout' in namespace 'std'; did you mean 'cout'?
4 | std::Cout << "Olá!\n";
| ^~~~
| cout
/usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/iostream:65:18: note: 'cout' declared here
65 | extern ostream cout; ///< Linked to standard output
| ^
1 error generated.
STD e #Include
$ g++ -std=c++23 -Wall -Wextra STD.cpp -o STD « STD::cout << "Olá!\n";
STD.cpp: In function ‘int main()’:
STD.cpp:4:5: error: ‘STD’ has not been declared
4 | STD::cout << "Olá!\n";
| ^~~
"STD não foi declarado": para o compilador, STD é um nome que ninguém criou.
$ g++ -std=c++23 -Wall -Wextra include.cpp -o include « primeira linha: #Include <iostream>
include.cpp:1:2: error: invalid preprocessing directive #Include; did you mean #include?
1 | #Include <iostream>
| ^~~~~~~
| include
include.cpp: In function ‘int main()’:
include.cpp:4:10: error: ‘cout’ is not a member of ‘std’
4 | std::cout << "Olá!\n";
| ^~~~
include.cpp:1:1: note: ‘std::cout’ is defined in header ‘<iostream>’; this is probably fixable by adding ‘#include <iostream>’
+++ |+#include <iostream>
1 | #Include <iostream>
Um erro só (o I maiúsculo) gerou dois. O primeiro é a causa: "diretiva de pré-processador inválida #Include; você quis dizer #include?". Como o iostream não foi incluído, o cout também "sumiu", e veio o segundo erro. Corrigindo o primeiro, os dois desaparecem. Essa é uma regra de ouro que você vai ver de novo: corrija sempre o primeiro erro primeiro.
Missão: este programa tem três maiúsculas no lugar errado. Rode, leia a primeira mensagem, corrija, e repita até ele mostrar as duas linhas. O editor usa o Clang, então as mensagens são parecidas com as do Clang acima.
#include <iostream>
int main() {
std::Cout << "Bom dia, Ana!\n";
Std::cout << "Hoje tem aula de C++.\n";
Return 0;
}
#include <iostream>
int main() {
std::cout << "Bom dia, Ana!\n";
std::cout << "Hoje tem aula de C++.\n";
return 0;
}
A regra das maiúsculas é para o código. O texto entre aspas é mostrado do jeito que está: "OLÁ", "olá" e "Olá" são três textos válidos, e cada um aparece na tela exatamente assim.
4. Espaços, linhas e recuo: o compilador não liga, mas você sim
Fora das aspas, o compilador trata espaços, tabulações e quebras de linha (os espaços em branco) como simples separadores. Um ou cem, tanto faz. Este programa "espalhado" compila e roda:
#include <iostream>
int
main
(
)
{
std::cout
<<
"Espalhado, mas funciona.\n"
;
}
$ g++ -std=c++23 -Wall -Wextra espalhado.cpp -o espalhado && ./espalhado Espalhado, mas funciona.
O espaço só é obrigatório onde ele separa duas palavras: int main não pode virar intmain. E ele é proibido dentro de um nome ou de um símbolo de dois caracteres: std::co ut, std: :cout e < < são erros.
O recuo (indentação) é para pessoas
Os quatro espaços antes de std::cout se chamam recuo ou indentação. O compilador os ignora, mas eles mostram, num relance, o que está dentro de quê. Compare:
| Sem recuo (compila, mas confunde) | Com recuo (o jeito certo) |
|---|---|
int main() {
std::cout << "Antes\n";
{
std::cout << "Dentro\n";
}
std::cout << "Depois\n";
} | int main() {
std::cout << "Antes\n";
{
std::cout << "Dentro\n";
}
std::cout << "Depois\n";
} |
A regra: a cada {, o recuo aumenta um nível; a cada }, diminui. O guia usa 4 espaços por nível. Também é comum ver 2 espaços ou uma tabulação. Escolha um jeito e use sempre o mesmo. Os editores de código (Editores e IDEs) recuam sozinhos e têm um comando "formatar documento", que arruma tudo de uma vez (por baixo, muitos usam a ferramenta clang-format).
Onde fica a {?
Outra questão de gosto. O guia põe a chave de abertura no fim da linha (int main() {). Muitos projetos a põem sozinha na linha de baixo:
#include <iostream>
int main()
{
std::cout << "Chave na linha de baixo também vale.\n";
}
$ g++ -std=c++23 -Wall -Wextra estilos.cpp -o estilos && ./estilos Chave na linha de baixo também vale.
Os dois estilos são corretos. Em um projeto que já existe, siga o estilo dele.
O texto entre aspas não pode quebrar de linha
Dentro das aspas, os espaços contam (eles aparecem na tela) e a quebra de linha é proibida. Se você apertar Enter no meio de um texto:
#include <iostream>
int main() {
std::cout << "Olá,
mundo!\n";
}
$ g++ -std=c++23 -Wall -Wextra quebra.cpp -o quebra
quebra.cpp:4:18: warning: missing terminating " character
4 | std::cout << "Olá,
| ^
quebra.cpp:4:18: error: missing terminating " character
4 | std::cout << "Olá,
| ^~~~~
quebra.cpp:5:7: error: stray ‘\’ in program
5 | mundo!\n";
| ^
quebra.cpp:5:9: warning: missing terminating " character
5 | mundo!\n";
| ^
quebra.cpp:5:9: error: missing terminating " character
5 | mundo!\n";
| ^~
quebra.cpp: In function ‘int main()’:
quebra.cpp:5:1: error: ‘mundo’ was not declared in this scope
5 | mundo!\n";
| ^~~~~
"Falta o caractere " de fechamento" na linha 4. Depois disso, o compilador se perde: lê mundo como se fosse código e reclama de tudo na linha 5. Mais uma vez, o primeiro erro é o que importa.
Para quebrar um texto longo em várias linhas do código, feche as aspas e abra de novo na linha de baixo. Dois textos entre aspas lado a lado, separados só por espaços, viram um só:
#include <iostream>
int main() {
std::cout << "Olá, "
"mundo!\n";
}
$ g++ -std=c++23 -Wall -Wextra juntas.cpp -o juntas && ./juntas Olá, mundo!
Repare que a saída ficou numa linha só: quem quebra a linha na tela é o \n, não o Enter no código (aula Novas Linhas).
5. std:: e o using namespace std;
Tudo o que vem da biblioteca padrão tem o "sobrenome" std: std::cout, std::cin, std::string. Esse sobrenome é um namespace ("espaço de nomes"), uma espécie de bairro onde os nomes moram. Ele existe para evitar confusão: se você criar no seu programa algo chamado count, ele não briga com o std::count da biblioteca, porque moram em bairros diferentes.
Esquecer o sobrenome dá erro:
#include <iostream>
int main() {
cout << "Olá!\n";
}
$ g++ -std=c++23 -Wall -Wextra semstd.cpp -o semstd
semstd.cpp: In function ‘int main()’:
semstd.cpp:4:5: error: ‘cout’ was not declared in this scope; did you mean ‘std::cout’?
4 | cout << "Olá!\n";
| ^~~~
| std::cout
In file included from semstd.cpp:1:
/usr/include/c++/15/iostream:65:18: note: ‘std::cout’ declared here
65 | extern ostream cout; ///< Linked to standard output
| ^~~~
"cout não foi declarado neste escopo; você quis dizer std::cout?" A note ainda mostra onde o std::cout de verdade está declarado, dentro do arquivo iostream da biblioteca. Você não precisa abrir esse arquivo; basta a sugestão.
O atalho: using namespace std;
Em muitos sites, livros e exercícios, você vai ver esta linha logo depois dos #include:
#include <iostream>
using namespace std;
int main() {
cout << "Olá com using!\n";
}
$ g++ -std=c++23 -Wall -Wextra using.cpp -o using && ./using Olá com using!
- using
- "Usando". Pede ao compilador que procure nomes em outro lugar também.
- namespace
- "Espaço de nomes": o que vem a seguir é um namespace inteiro, não um nome só.
- std
- O namespace da biblioteca padrão.
- ;
- É uma instrução, então termina com ponto e vírgula.
Em português: "a partir daqui, quando eu escrever um nome solto e você não achar, procure também no std". É como derrubar o muro entre o seu bairro e o bairro std. Funciona, e economiza digitação. O problema é que a biblioteca padrão tem milhares de nomes, muitos deles palavras comuns em inglês: size, count, max, list, data. Com o muro derrubado, um nome seu pode trombar com um deles. A Ana criou uma variável chamada size ("tamanho"):
#include <iostream>
using namespace std;
int size = 3;
int main() {
cout << "Tamanho: " << size << "\n";
}
$ g++ -std=c++23 -Wall -Wextra ambiguo.cpp -o ambiguo
ambiguo.cpp: In function ‘int main()’:
ambiguo.cpp:7:28: error: reference to ‘size’ is ambiguous
7 | cout << "Tamanho: " << size << "\n";
| ^~~~
In file included from /usr/include/c++/15/string:55,
from /usr/include/c++/15/bits/locale_classes.h:42,
from /usr/include/c++/15/bits/ios_base.h:43,
from /usr/include/c++/15/ios:46,
from /usr/include/c++/15/bits/ostream.h:43,
from /usr/include/c++/15/ostream:42,
from /usr/include/c++/15/iostream:43,
from ambiguo.cpp:1:
/usr/include/c++/15/bits/range_access.h:284:5: note: candidates are: ‘template<class _Tp, long unsigned int _Nm> constexpr std::size_t std::size(const _Tp (&)[_Nm])’
284 | size(const _Tp (&)[_Nm]) noexcept
| ^~~~
/usr/include/c++/15/bits/range_access.h:274:5: note: ‘template<class _Container> constexpr decltype (__cont.size()) std::size(const _Container&)’
274 | size(const _Container& __cont) noexcept(noexcept(__cont.size()))
| ^~~~
ambiguo.cpp:4:5: note: ‘int size’
4 | int size = 3;
| ^~~~
Assustador, mas a primeira linha de erro resume tudo: "a referência a size é ambígua". O compilador achou dois size: o da Ana (a última note, linha 4) e o std::size, uma função da biblioteca que ela nem sabia que existia (as note do meio mostram onde ele mora, dentro dos arquivos que o iostream puxa). Ele não sabe qual usar e desiste. Sem o using namespace std;, o mesmo programa (com std::cout) compila sem problema, porque o size da Ana e o std::size continuam em bairros separados.
Em programas de 10 linhas, o risco é pequeno. Em programas grandes, com vários arquivos e bibliotecas, essas trombadas aparecem de repente, às vezes quando você atualiza o compilador e a biblioteca ganha um nome novo. Num arquivo de cabeçalho (.h), o using namespace std; é pior ainda, porque derruba o muro em todo arquivo que o incluir (aula Arquivos .h e .cpp).
Formas de escrever, e qual usar
| Forma | Exemplo | Quando |
|---|---|---|
| Nome completo | std::cout << "Oi\n"; | A forma do guia. Sempre claro de onde o nome vem. |
| Trazer um nome só | using std::cout; e depois cout << "Oi\n"; | Quando um nome se repete muito num arquivo .cpp. Traz só ele, não os milhares. |
| Trazer o namespace inteiro | using namespace std; | Exercícios curtos e código de outras pessoas. Evite em programas grandes e nunca use em .h. |
#include <iostream>
using std::cout;
int main() {
cout << "Só o cout veio para cá.\n";
}
$ g++ -std=c++23 -Wall -Wextra usingcout.cpp -o usingcout && ./usingcout Só o cout veio para cá.
Os namespaces (inclusive como criar os seus) têm aula própria no Nível 2: Namespaces.
Quando dá errado
error: expected ‘;’ before …
#include <iostream>
int main() {
std::cout << "Olá, mundo!\n"
}
$ g++ semponto.cpp -o semponto
semponto.cpp: In function ‘int main()’:
semponto.cpp:4:33: error: expected ‘;’ before ‘}’ token
4 | std::cout << "Olá, mundo!\n"
| ^
| ;
5 | }
| ~
O que significa: "esperava ; antes de }". Uma instrução não foi terminada. O g++ indica o lugar e até desenha o ; que falta.
Como consertar: ponha o ; no fim da instrução indicada. Se a linha apontada parece certa, olhe o fim da linha de cima.
‘Cout’ is not a member of ‘std’; did you mean ‘cout’?
O que significa: um nome da biblioteca foi escrito com maiúscula (ou com uma letra trocada). Mensagens com did you mean ("você quis dizer") trazem a correção pronta.
Como consertar: escreva o nome como sugerido, em minúsculas.
‘cout’ was not declared in this scope; did you mean ‘std::cout’?
O que significa: faltou o std:: na frente do nome.
Como consertar: escreva std::cout. (O using namespace std; também "resolve", mas traz os riscos da seção 5.)
referência não definida para "main" / undefined reference to `main'
O que significa: o programa não tem uma função chamada exatamente main. Quase sempre é Main, mian ou MAIN.
Como consertar: int main(), tudo em minúsculas.
missing terminating " character
O que significa: um texto abriu aspas e não fechou na mesma linha. Ou faltou a " final, ou você quebrou a linha no meio do texto.
Como consertar: feche as aspas na mesma linha. Para textos longos, use dois textos lado a lado ("Olá, " "mundo!\n").
reference to ‘size’ is ambiguous
O que significa: com using namespace std;, um nome seu ficou igual a um nome da biblioteca padrão.
Como consertar: tire o using namespace std; e use std::, ou dê outro nome à sua variável (tamanho, em vez de size).
Referência rápida
| Regra | Certo | Errado |
|---|---|---|
Instrução termina com ; | std::cout << "Oi\n"; | std::cout << "Oi\n" |
Sem ; em linhas com # | #include <iostream> | #include <iostream>; (o g++ avisa: extra tokens at end of ‘#include’ directive) |
Toda { tem sua } | int main() { … } | int main() { … |
| Maiúsculas importam | main, std::cout, return | Main, std::Cout, Return |
| Espaços e linhas: livres fora das aspas | std::cout << "Oi\n"; | std::co ut, < < |
| Texto não quebra de linha | "Olá, " "mundo" | "Olá, + Enter + mundo" |
| Recuo de 4 espaços por bloco | Não muda o programa; muda quem lê | |
Nomes da biblioteca com std:: | std::cout | cout (sem using) |
Exercícios
1. O que falta?
Complete o símbolo que termina esta instrução:
2. O sobrenome
Complete para usar o cout da biblioteca padrão sem using:
3. Trazer um nome só
Complete para trazer só o cout para o seu arquivo (e não o namespace inteiro):
4. Qual destas linhas compila?
5. O que o compilador faz com o recuo (os espaços no começo das linhas)?
6. Por que o guia evita using namespace std;?
Missão final: arrume a bagunça
Missão: a Ana escreveu este programa com pressa. Ele tem um erro de maiúscula, um ; faltando e usa using namespace std;. Faça ele mostrar as três linhas, sem o using namespace std; (use std::cout), e arrume o recuo para 4 espaços.
Lista da feira - tomate - alface
#include <iostream>
using namespace std;
int main() {
cout << "Lista da feira\n"
Cout << "- tomate\n";
cout << "- alface\n";
}
#include <iostream>
int main() {
std::cout << "Lista da feira\n";
std::cout << "- tomate\n";
std::cout << "- alface\n";
}
Resumo
- Uma instrução é uma ordem completa e termina com
;. Linhas com#e a}de uma função não levam;. - Blocos agrupam instruções entre
{ }. Toda chave aberta precisa ser fechada. - Maiúsculas importam:
main,std::cout,return,#include, tudo em minúsculas. - Espaços, linhas em branco e recuo não mudam o programa, mas mudam muito a leitura. Use 4 espaços por nível.
- Um texto entre aspas não pode quebrar de linha no código; para isso, use dois textos lado a lado.
std::é o "sobrenome" da biblioteca padrão.using namespace std;derruba o muro e pode causar trombadas de nomes; o guia usastd::.- Corrija sempre o primeiro erro: muitas vezes ele causa os outros.