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 dechar, 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
intsão promovidos aintantes 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,
intde 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).