Então você acha que sabe C? (2016)

Um quiz popular sobre C que gira em torno de comportamento indefinido e definido pela implementação reacendeu o debate sobre o que significa “saber C”. Os comentários analisam casos extremos envolvendo tamanhos de inteiros, padding de `struct`, codificação de caracteres, shifts e múltiplos incrementos, discutindo quando as respostas são realmente impossíveis de saber sem especificar plataforma, compilador e versão do padrão. Muitos veem esses quebra-cabeças como pedantes ou enganosos em comparação com preocupações do mundo real, como portabilidade, testes nos sistemas-alvo e evitar código ilegível, enquanto outros os defendem como lembretes valiosos de como suposições de baixo nível podem ser frágeis.

Reação geral ao quiz

  • Muitos veem o quiz como um exercício de “pegadinha” sobre casos extremos de C e comportamento indefinido, em vez de um teste prático de habilidades.
  • Alguns gostaram dele como um lembrete das sutilezas de C; outros o acharam pedante, irritante ou uma “perda de tempo” assim que perceberam que o metajogo era “a resposta correta é: você não pode saber”.
  • Uma reclamação recorrente: a opção de resposta “não sei” é ambígua; as pessoas queriam escolhas explícitas como “indefinido”, “não especificado” ou “definido pela implementação”.

Comportamento indefinido / definido pela implementação / não especificado

  • Vários comentários dissecam se perguntas específicas envolvem comportamento indefinido, comportamento definido pela implementação ou comportamento não especificado.
  • Há desacordo direto sobre a primeira pergunta: alguns a chamam de indefinida, outros de definida pela implementação, outros de “meramente não especificada”, e alguns apontam que a própria explicação do artigo assume sizeof(int) == 4, o que não é garantido.
  • O layout de struct, as larguras de inteiros, a assinatura de char, a codificação de caracteres (ASCII vs EBCDIC) e shifts por ≥ largura de bits são citados como exemplos de onde o padrão deliberadamente deixa o comportamento não portátil.

Discussões técnicas concretas

  • Promoções de inteiros: tipos menores que int são promovidos a int antes da aritmética, levando a locais surpreendentes de UB (por exemplo, exemplos de multiplicação em 16 bits vs 32 bits).
  • Shifts: deslocar por ≥ a largura do tipo é UB em C e C++; CPUs diferentes tratam isso de formas diferentes (mascaramento, zero, trap).
  • Expressões com múltiplos incrementos (i++ + ++i, swap por XOR encadeado) são destacadas como UB clássico relacionado a pontos de sequência.
  • Laços infinitos: C e C++ diferem em quando compiladores podem otimizar laços sem efeitos observáveis; isso importa para código embarcado.
  • Type punning via union é válido em C, mas não é garantido em C++.

Praticidade vs pureza

  • Um grupo argumenta que, em projetos reais, você mira plataformas conhecidas, confia em normas de fato (por exemplo, int de 32 bits, ASCII, IEEE-754) e apoia isso com testes; conformidade estrita com o padrão em todos os casos extremos é exagero.
  • Outro grupo enfatiza que depender de comportamento não garantido pode quebrar com novos compiladores, flags, arquiteturas ou alvos embarcados; quizzes assim destacam suposições perigosas.

Linguagens, ferramentas e alternativas

  • A discussão se expande para peculiaridades de C++, sintaxe de cast e dimensionamento de inteiros em D, métodos de shift mais seguros em Rust, Scheme via Gambit-C e uso de GC em C.
  • Vários argumentam que há recursos educacionais melhores: boas práticas, gerenciamento de memória, idioms mais seguros e livros (incluindo o PDF linkado do mesmo site).