C Versões
O C de hoje não é igual ao de 1972. A linguagem ganhou versões novas ao longo dos anos, cada uma com um nome curto como C99 ou C23 (o número é o ano). Quando você procura ajuda na internet, encontra código de todas as épocas, e um programa escrito para uma versão às vezes não compila em outra. Nesta aula você vai conhecer cada versão, saber qual o seu compilador está usando e aprender a escolher a versão com a opção -std=.
- O que é o padrão do C e quem o decide.
- As versões: K&R C, C89/C90, C99, C11, C17, C23 e o próximo, C2y, com o que cada uma trouxe.
- A diferença entre a versão do C e a versão do compilador.
- Como descobrir a versão com
__STDC_VERSION__. - Como escolher a versão com
-std=c23,-std=c17… e o que muda comgnu23e-pedantic. - Os erros que aparecem quando o código e a versão não combinam.
A ideia: as regras do futebol
O futebol tem um livro de regras oficial, e de tempos em tempos ele muda: um ano surge o cartão amarelo, outro ano o goleiro deixa de poder pegar com a mão a bola recuada pelo companheiro, depois chega o árbitro de vídeo. Um jogo de 1970 e um de hoje são "futebol", mas algumas jogadas de hoje seriam estranhas em 1970, e algumas de 1970 hoje dariam falta.
O C é igual. Existe um livro de regras oficial, o padrão, que diz exatamente o que é um programa C válido e o que ele deve fazer. De tempos em tempos sai uma edição nova. Os compiladores (gcc, Clang, o da Microsoft) são como os árbitros: cada um aplica as regras, e você pode dizer a ele qual edição do livro usar.
O padrão ISO
Nos primeiros anos, o C não tinha regras oficiais: a referência era o livro de Kernighan e Ritchie, e cada fabricante de compilador fazia pequenas mudanças. Um programa que funcionava num computador podia não compilar em outro. Para acabar com a confusão, o instituto americano de normas (ANSI) reuniu um comitê e publicou, em 1989, o primeiro padrão do C. No ano seguinte, a ISO (a Organização Internacional de Normalização, a mesma das normas ISO de empresas) adotou o mesmo texto.
Desde então, o C é mantido por um comitê internacional, o WG14, com gente de empresas, universidades e projetos de compiladores. O documento oficial se chama ISO/IEC 9899; cada edição tem o ano no fim (por exemplo, ISO/IEC 9899:2024 é o C23). O comitê é bem conservador: o C muda devagar, e quase todo programa antigo continua compilando nas versões novas. Isso é proposital: existe muito código em C funcionando há décadas em aviões, bancos e sistemas operacionais.
O que cada versão trouxe
O nome curto de cada versão é o ano em que ela ficou pronta (às vezes um ano antes de ser publicada oficialmente). Você não precisa decorar a tabela: ela serve para consultar quando uma aula mostrar o selo de versão, ou quando você encontrar código antigo.
| Versão | Ano | O que trouxe (os destaques) |
|---|---|---|
| K&R C | 1978 | Não era um padrão: era o C descrito no livro The C Programming Language, de Kernighan e Ritchie. Funções sem tipo nos parâmetros, int subentendido quando o tipo era omitido, nada de void nem const. |
| C89 / C90 (ANSI C) | 1989 / 1990 | O primeiro padrão. Protótipos (int soma(int a, int b), com o tipo de cada parâmetro), void, const, volatile, e a biblioteca padrão oficial: stdio.h, stdlib.h, string.h, math.h… C89 (ANSI) e C90 (ISO) são o mesmo texto. |
| C95 | 1995 | Um complemento pequeno: caracteres "largos" (wchar.h), para idiomas com muitos símbolos. |
| C99 | 1999 | Uma grande atualização. Comentários de linha //; declarar variáveis em qualquer ponto e dentro do for (for (int i = 0; …)); bool, true e false com #include <stdbool.h>; long long; inteiros de tamanho fixo (stdint.h: int32_t, uint8_t…); snprintf; inline; arrays de tamanho variável (VLAs); o main pode omitir o return 0. |
| C11 | 2011 | _Generic (escolher o código pelo tipo), _Static_assert (conferir uma condição na hora de compilar), _Noreturn, _Alignas/_Alignof, threads (threads.h) e operações atômicas, textos UTF-8 com u8"…". E retirou o perigoso gets da biblioteca. |
| C17 (ou C18) | 2017 / 2018 | Nenhum recurso novo: só correções e esclarecimentos no texto do C11. Ficou pronto em 2017 e foi publicado em 2018, por isso tem dois nomes. |
| C23 | 2023 / 2024 | bool, true e false viram palavras da linguagem (sem #include); nullptr; constexpr; typeof; auto para deduzir o tipo; atributos como [[nodiscard]]; static_assert como palavra-chave; números binários (0b1010) e separador de dígitos (1'000'000); #embed (embutir um arquivo no programa); _BitInt(N). E removeu o jeito K&R de definir funções. Ficou pronto em 2023 e foi publicado em outubro de 2024. É a versão que este guia ensina. |
| C2y | (em andamento) | A próxima versão, ainda sendo escrita pelo comitê. O "y" é um ano que ainda não se sabe. O gcc 15 já aceita -std=c2y para quem quer testar os primeiros recursos. |
O C23 numa caixa
Este programa usa sete novidades do C23 de uma vez. Não se preocupe em entender cada uma: todas têm a sua aula (Booleanos, Ponteiro Nulo, Constantes, static_assert, Novidades do C99 ao C23). O que importa aqui é ver que o compilador do guia entende todas:
Experimente: rode e veja as cinco linhas. Depois troque 0b1010 por 0b1111 (o número 15 escrito em binário) e rode de novo.
#include <stdio.h>
int main(void) {
constexpr int ANO = 2024;
bool aprovada = true;
int *nada = nullptr;
auto populacao = 1'000'000;
typeof(populacao) dobro = populacao * 2;
int mascara = 0b1010;
static_assert(sizeof(int) >= 4);
printf("C23 publicado em %d\n", ANO);
printf("aprovada = %d\n", aprovada);
printf("nada é nulo? %d\n", nada == nullptr);
printf("dobro = %d\n", dobro);
printf("mascara = %d\n", mascara);
return 0;
}
No terminal da Ana, com o gcc 15 e o C23, a saída é a mesma:
$ gcc -std=c23 -Wall -Wextra c23.c -o c23 && ./c23 C23 publicado em 2024 aprovada = 1 « true aparece como 1 nada é nulo? 1 dobro = 2000000 mascara = 10 « 0b1010 em binário é 10
E com uma versão mais antiga? A opção -std=c17 pede ao gcc as regras do C17. O mesmo arquivo dá uma enxurrada de erros (aqui, só o começo):
$ gcc -std=c17 -Wall -Wextra c23.c -o c23
c23.c: In function ‘main’:
c23.c:4:5: error: ‘constexpr’ undeclared (first use in this function) « o C17 não conhece constexpr
4 | constexpr int ANO = 2024;
| ^~~~~~~~~
c23.c:4:5: note: each undeclared identifier is reported only once for each function it appears in
c23.c:4:14: error: expected ‘;’ before ‘int’
4 | constexpr int ANO = 2024;
| ^~~~
| ;
c23.c:5:5: error: unknown type name ‘bool’ « nem bool sem include
5 | bool aprovada = true;
| ^~~~
c23.c:2:1: note: ‘bool’ is defined in header ‘<stdbool.h>’; this is probably fixable by adding ‘#include <stdbool.h>’
1 | #include <stdio.h>
+++ |+#include <stdbool.h>
2 |
c23.c:5:21: error: ‘true’ undeclared (first use in this function)
5 | bool aprovada = true;
| ^~~~
c23.c:5:21: note: ‘true’ is defined in header ‘<stdbool.h>’; this is probably fixable by adding ‘#include <stdbool.h>’
c23.c:6:17: error: ‘nullptr’ undeclared (first use in this function) « nem nullptr
6 | int *nada = nullptr;
| ^~~~~~~
Para o C17, palavras como constexpr e nullptr são nomes desconhecidos, como se a Ana tivesse inventado uma palavra. Repare que o gcc é gentil com o bool: ele diz que bool é definido no arquivo <stdbool.h> e até mostra onde acrescentar o #include (a linha com +++). No C99, C11 e C17, era assim que se usava bool.
Padrão × compilador
Uma confusão muito comum: a versão do C e a versão do compilador são coisas diferentes. O C23 é o livro de regras; o gcc 15 é um programa que segue esse livro (e os anteriores). Cada compilador tem a sua própria numeração, e cada versão nova de compilador aprende mais recursos das versões novas do C.
Ver a versão do compilador
$ gcc --version gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0 « o compilador está na versão 15.2 Copyright (C) 2025 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ clang --version Ubuntu clang version 21.1.8 (6ubuntu1) « o Clang está na versão 21.1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/lib/llvm-21/bin
Nenhum dos dois diz "C23": o número 15.2 é do gcc, não do C. Para saber qual versão do C está valendo, é preciso perguntar ao próprio código.
Ver a versão do C: __STDC_VERSION__
Todo compilador de C define uma palavra especial chamada __STDC_VERSION__ (com dois sublinhados antes e dois depois; STDC = standard C, "C padrão"). Ela vale um número no formato ano e mês da versão do C que está sendo usada:
#include <stdio.h>
int main(void) {
printf("__STDC_VERSION__ = %ld\n", __STDC_VERSION__);
return 0;
}
- __STDC_VERSION__
- Uma macro pré-definida: o pré-processador troca essa palavra por um número antes de compilar (como o
NOMEda aula Como o C Vira Programa). O número é do tipolong: no C23, é202311L(oLno fim marca umlong). - %ld
- O "buraco" do
printfpara umlong(long decimal). A aula Formatos do printf mostra todos.
A Ana compilou o mesmo arquivo pedindo cada versão:
$ gcc versao.c -o versao && ./versao
__STDC_VERSION__ = 202311 « sem -std: o gcc 15 já usa o C23
$ gcc -std=c23 versao.c -o versao && ./versao
__STDC_VERSION__ = 202311 « C23: novembro de 2023
$ gcc -std=c17 versao.c -o versao && ./versao
__STDC_VERSION__ = 201710 « C17: outubro de 2017
$ gcc -std=c11 versao.c -o versao && ./versao
__STDC_VERSION__ = 201112 « C11: dezembro de 2011
$ gcc -std=c99 versao.c -o versao && ./versao
__STDC_VERSION__ = 199901 « C99: janeiro de 1999
$ gcc -std=c2y versao.c -o versao && ./versao
__STDC_VERSION__ = 202500 « C2y: número provisório
$ gcc -std=c89 versao.c -o versao && ./versao
versao.c: In function ‘main’:
versao.c:4:40: error: ‘__STDC_VERSION__’ undeclared (first use in this function)
4 | printf("__STDC_VERSION__ = %ld\n", __STDC_VERSION__);
| ^~~~~~~~~~~~~~~~
versao.c:4:40: note: each undeclared identifier is reported only once for each function it appears in
Repare no último: no C89, o __STDC_VERSION__ nem existia (ele surgiu no complemento de 1995), então o compilador diz que é um nome não declarado. Os números servem para o código descobrir em que versão está, com #if __STDC_VERSION__ >= 202311L (aula Compilação Condicional).
Dá para perguntar o valor direto ao pré-processador. A opção -dM lista todas as macros pré-definidas, e o grep filtra a que interessa:
$ gcc -dM -E -x c /dev/null | grep STDC_VERSION #define __STDC_VERSION__ 202311L $ clang -dM -E -x c /dev/null | grep STDC_VERSION #define __STDC_VERSION__ 201710L « o Clang 21 ainda usa o C17 se você não pedir
O -x c diz "trate como C" e o /dev/null é um arquivo vazio especial do Linux: é um jeito de ver as macros sem ter um programa.
A versão de cada compilador, quando você não pede nenhuma
Se você não usar -std=, cada compilador escolhe uma versão por conta própria, e essa escolha muda com o tempo:
| Compilador | Versão do C usada sem -std= |
|---|---|
| gcc 5 a 7 | gnu11 (C11 com extensões do GNU) |
| gcc 8 a 14 | gnu17 |
| gcc 15 (o do guia) | gnu23, conferido acima: 202311 |
| Clang 21 (conferido acima) | gnu17: 201710 |
| Editor do guia (Clang 22) | Sempre -std=c23, que o guia escolhe por você |
É por isso que o guia sempre escreve o -std=c23 no comando: assim o programa se comporta igual no gcc 15, no gcc 14 (que já aceita a opção) e no Clang, em vez de depender do que cada um escolhe sozinho.
Missão: o editor compila com -std=c23. Faça o programa mostrar isso: troque o ??? por %ld e acrescente , __STDC_VERSION__ antes do ), como no versao.c lá em cima. A saída deve ser Este código foi compilado com 202311.
#include <stdio.h>
int main(void) {
printf("Este código foi compilado com ???\n");
return 0;
}
#include <stdio.h>
int main(void) {
printf("Este código foi compilado com %ld\n", __STDC_VERSION__);
return 0;
}
Escolher a versão: as opções -std=
A opção -std= (de standard, padrão) diz ao compilador qual livro de regras usar. Existem dois "sabores" de cada versão:
| Opção | O que pede |
|---|---|
-std=c23 | O C23 do padrão ISO. Desliga algumas extensões do gcc que entram em conflito com o padrão. |
-std=gnu23 | O C23 mais as extensões do GNU (recursos que só o gcc e o Clang têm). É o que o gcc 15 usa se você não pedir nada. |
-std=c17 / -std=gnu17 | O C17 (também aceita c18, que é o mesmo). |
-std=c11, -std=c99, -std=c89 | As versões anteriores (c90 e ansi são o mesmo que c89). |
-std=c2y | A próxima versão, experimental. |
-std=c2x | Nome antigo do c23, de antes de o ano ser conhecido. Ainda funciona e dá o mesmo 202311. |
Na prática, c23 e gnu23 quase nunca fazem diferença em programas de iniciante. A diferença aparece quando o código usa uma extensão: um recurso que o gcc oferece, mas que não está (ou não estava) no padrão. Por exemplo, os números binários 0b1010 só entraram no padrão no C23, mas o gcc os aceita há muitos anos como extensão. Para o gcc avisar quando o código sai do padrão escolhido, existe a opção -pedantic ("detalhista"):
$ gcc -std=c17 -Wall -Wextra binario.c -o binario && ./binario
mascara = 10 « o gcc aceitou, calado
$ gcc -std=c17 -Wall -Wextra -pedantic binario.c -o binario
binario.c: In function ‘main’:
binario.c:4:19: warning: binary constants are a C23 feature or GCC extension [-Wpedantic]
4 | int mascara = 0b1010;
| ^~~~~~
$ gcc -std=c23 -Wall -Wextra -pedantic binario.c -o binario && ./binario
mascara = 10 « no C23 é padrão: nenhum aviso
O aviso diz: "constantes binárias são um recurso do C23 ou uma extensão do GCC". Ou seja, o código funciona no gcc, mas outro compilador seguindo o C17 poderia recusá-lo. O -pedantic é útil quando o programa precisa compilar em muitos compiladores diferentes. Neste guia, o comando padrão é gcc -std=c23 -Wall -Wextra (a aula Compilar com gcc explica cada parte).
Qual versão usar?
| Situação | Use |
|---|---|
| Aprendendo, ou começando um programa novo | -std=c23, como o guia |
O compilador é antigo e não aceita c23 (gcc 13 ou anterior) | -std=c2x (gcc 9 a 13, com parte do C23) ou -std=c17 |
| Projeto que já existe | A versão que o projeto usa (está no Makefile ou no CMakeLists.txt) |
| Microcontroladores e compiladores de fabricantes | Muitas vezes c99 ou c11: é o que essas ferramentas aceitam |
| Código que precisa compilar em qualquer lugar | Uma versão mais antiga com -pedantic |
O selo de versão do guia
Quando uma aula mostra um recurso que não existe em todas as versões, ela coloca um selo ao lado, assim: C99, C11, C23. O selo diz a primeira versão que tem o recurso. Por exemplo, bool sem #include C23 quer dizer: "funciona no C23 e nas seguintes; nas anteriores, use #include <stdbool.h>". A aula sempre diz a alternativa antiga, porque muito código e muitos cursos ainda usam C99 ou C11.
A caixa abaixo foi configurada para compilar com -std=c17, como um compilador um pouco mais antigo. Veja o que acontece com o bool:
Missão: esta caixa compila com -std=c17. Rode e veja o erro: o C17 não conhece bool sozinho. Conserte do jeito do C99 ao C17: acrescente a linha #include <stdbool.h> logo abaixo do #include <stdio.h>. Rode de novo: deve aparecer aprovada = 1.
#include <stdio.h>
int main(void) {
bool aprovada = true;
printf("aprovada = %d\n", aprovada);
return 0;
}
#include <stdio.h>
#include <stdbool.h>
int main(void) {
bool aprovada = true;
printf("aprovada = %d\n", aprovada);
return 0;
}
No editor, o Clang diz use of undeclared identifier 'bool' ("uso de identificador não declarado"); o gcc diz unknown type name ‘bool’ ("nome de tipo desconhecido") e sugere o stdbool.h. No C23, as duas versões do programa funcionam: o #include <stdbool.h> continua permitido, só não é mais necessário.
Quando dá errado
A Ana compilou um programa com for (int i…) e comentário // num projeto antigo, que usava -std=c89:
$ gcc -std=c89 -Wall -Wextra antigo.c -o antigo
antigo.c: In function ‘main’:
antigo.c:4:5: error: C++ style comments are not allowed in ISO C90
4 | // conta de 1 a 3
| ^
antigo.c:4:5: note: (this will be reported only once per input file)
antigo.c:5:5: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
5 | for (int i = 1; i <= 3; i++) {
| ^~~
antigo.c:5:5: note: use option ‘-std=c99’, ‘-std=gnu99’, ‘-std=c11’ or ‘-std=gnu11’ to compile your code
O que aconteceu: o primeiro erro diz "comentários no estilo C++ não são permitidos no ISO C90" (o // veio do C++ e só entrou no C99). O segundo: "declarações iniciais no laço for só são permitidas no modo C99 ou C11", e o gcc ainda sugere as opções que resolvem.
Como consertar: se puder, use uma versão nova (-std=c23). Se o projeto exige C89, use /* comentário */ e declare a variável antes do laço: int i; e depois for (i = 1; …). Você vai ver muito código assim em projetos antigos.
A Ana copiou o primeiro programa do livro de 1978, do jeito que ele aparece nas edições antigas:
#include <stdio.h>
main()
{
printf("hello, world\n");
}
$ gcc hello.c -o hello
hello.c:3:1: error: return type defaults to ‘int’ [-Wimplicit-int]
3 | main()
| ^~~~
$ gcc -std=c89 hello.c -o hello && ./hello « com as regras de 1989, passa
hello, world
O que aconteceu: no C antigo, quem não escrevia o tipo ganhava um int de presente: main() queria dizer int main(). O C99 proibiu isso, e o gcc 14 passou a tratar como erro: "o tipo de retorno fica int por padrão". Com -std=c89, o gcc volta às regras antigas e aceita.
Como consertar: escreva o tipo: int main(void), como em todos os exemplos do guia. Ao copiar código antigo da internet, espere pequenas adaptações como essa.
O que aconteceu: sem -std=, cada compilador usa uma versão diferente (veja a tabela acima). Um programa com bool sem #include, ou com nullptr, compila no gcc 15 (que usa C23 sozinho), mas falha no gcc 13 ou no Clang 21, que usam C17 se ninguém pedir outra coisa.
Como consertar: escreva sempre a versão no comando: gcc -std=c23 …. Se o compilador do colega for antigo demais para o C23, ele vai dizer isso claramente (unrecognized command-line option), em vez de dar erros confusos no meio do código.
Referência
| Versão | __STDC_VERSION__ | Opção do gcc | Documento |
|---|---|---|---|
| K&R C | (não existe) | (nenhuma) | Livro de 1978 |
| C89 / C90 | (não existe) | -std=c89, -std=c90, -ansi | ANSI X3.159-1989 / ISO/IEC 9899:1990 |
| C95 | 199409L | -std=iso9899:199409 | ISO/IEC 9899/AMD1:1995 |
| C99 | 199901L | -std=c99 | ISO/IEC 9899:1999 |
| C11 | 201112L | -std=c11 | ISO/IEC 9899:2011 |
| C17 | 201710L | -std=c17, -std=c18 | ISO/IEC 9899:2018 |
| C23 | 202311L | -std=c23 (gcc 14+; -std=c2x nos anteriores) | ISO/IEC 9899:2024 |
| C2y | 202500L (provisório, gcc 15) | -std=c2y | (em andamento) |
| Comando | Para quê |
|---|---|
gcc --version | Versão do compilador (não do C) |
gcc -dM -E -x c /dev/null | grep STDC_VERSION | Versão do C que o gcc usa sem -std= |
gcc -std=c23 … | Escolher o C23 (ou outra versão) |
gcc -std=c23 -pedantic … | Avisar quando o código usa extensões fora do padrão |
Exercícios
1. O que quer dizer o número em "C99" ou "C23"?
2. Em que versão os comentários de linha // e o for (int i = 0; …) passaram a ser permitidos?
3. O gcc --version mostra 15.2.0. O que isso diz?
4. Qual destas é uma novidade do C23?
5. A Ana compilou sem -std= e o programa mostrou __STDC_VERSION__ = 202311. O que ela pode concluir?
6. Escolher o C17
Complete o comando para compilar com as regras do C17:
7. A macro da versão
Qual é o nome da macro que guarda a versão do C usada na compilação? Complete:
8. O include do bool antigo
No C99, C11 e C17, qual arquivo é preciso incluir para usar bool, true e false?
Resumo
- O C tem um padrão oficial (ISO/IEC 9899), mantido por um comitê internacional. Cada edição tem um nome com o ano: C89/C90, C99, C11, C17, C23. Antes disso, havia o K&R C do livro de 1978; depois vem o C2y.
- C99 trouxe
//, variáveis nofor,stdbool.hestdint.h; C11 trouxe_Generic,_Static_asserte threads; C17 só corrigiu; C23 trouxeboolsem include,nullptr,constexpr,typeof,auto, atributos,0b1010e1'000. - A versão do compilador (gcc 15.2) não é a versão do C. A versão do C aparece em
__STDC_VERSION__:202311Lé o C23. - Sem
-std=, o gcc 15 usagnu23; o Clang 21 usagnu17. Por isso o guia sempre escreve-std=c23. gnu23= C23 com extensões do GNU;-pedanticavisa quando o código sai do padrão.- Código antigo (sem tipo no
main, funções no estilo K&R) pode não compilar nos compiladores modernos; código novo (//,bool,nullptr) não compila com-std=antigo. - Próximo passo: instalar o compilador no seu computador, em Instalar no Linux ou Instalar no Windows e macOS.