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=.

Você vai aprender
  • 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 com gnu23 e -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ãoAnoO que trouxe (os destaques)
K&R C1978Nã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 / 1990O 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.
C951995Um complemento pequeno: caracteres "largos" (wchar.h), para idiomas com muitos símbolos.
C991999Uma 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.
C112011_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 / 2018Nenhum 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.
C232023 / 2024bool, 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 NOME da aula Como o C Vira Programa). O número é do tipo long: no C23, é 202311L (o L no fim marca um long).
%ld
O "buraco" do printf para um long (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).

Sem compilar nada

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:

CompiladorVersão do C usada sem -std=
gcc 5 a 7gnu11 (C11 com extensões do GNU)
gcc 8 a 14gnu17
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çãoO que pede
-std=c23O C23 do padrão ISO. Desliga algumas extensões do gcc que entram em conflito com o padrão.
-std=gnu23O 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=gnu17O C17 (também aceita c18, que é o mesmo).
-std=c11, -std=c99, -std=c89As versões anteriores (c90 e ansi são o mesmo que c89).
-std=c2yA próxima versão, experimental.
-std=c2xNome 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çãoUse
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á existeA versão que o projeto usa (está no Makefile ou no CMakeLists.txt)
Microcontroladores e compiladores de fabricantesMuitas vezes c99 ou c11: é o que essas ferramentas aceitam
Código que precisa compilar em qualquer lugarUma 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

Código novo num compilador configurado para uma versão antiga

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.

O "hello, world" original do livro K&R não compila mais

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.

"Funciona no meu computador e não no do meu colega"

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 gccDocumento
K&R C(não existe)(nenhuma)Livro de 1978
C89 / C90(não existe)-std=c89, -std=c90, -ansiANSI X3.159-1989 / ISO/IEC 9899:1990
C95199409L-std=iso9899:199409ISO/IEC 9899/AMD1:1995
C99199901L-std=c99ISO/IEC 9899:1999
C11201112L-std=c11ISO/IEC 9899:2011
C17201710L-std=c17, -std=c18ISO/IEC 9899:2018
C23202311L-std=c23 (gcc 14+; -std=c2x nos anteriores)ISO/IEC 9899:2024
C2y202500L (provisório, gcc 15)-std=c2y(em andamento)
ComandoPara quê
gcc --versionVersão do compilador (não do C)
gcc -dM -E -x c /dev/null | grep STDC_VERSIONVersã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:

gcc -Wall -Wextra ola.c -o ola

7. A macro da versão

Qual é o nome da macro que guarda a versão do C usada na compilação? Complete:

printf("%ld\n", );

8. O include do bool antigo

No C99, C11 e C17, qual arquivo é preciso incluir para usar bool, true e false?

#include <>

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 no for, stdbool.h e stdint.h; C11 trouxe _Generic, _Static_assert e threads; C17 só corrigiu; C23 trouxe bool sem include, nullptr, constexpr, typeof, auto, atributos, 0b1010 e 1'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 usa gnu23; o Clang 21 usa gnu17. Por isso o guia sempre escreve -std=c23.
  • gnu23 = C23 com extensões do GNU; -pedantic avisa 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.