3 pontos por GN⁺ 2024-10-16 | 1 comentários | Compartilhar no WhatsApp
  • A edição revisada de Modern C foi disponibilizada para download gratuito, permitindo revisar materiais de estudo e referência da linguagem C com base no C23
  • O foco central da revisão é incorporar o C23, ajudando na transição para o novo padrão em sintonia com o cronograma e o momento do processo de publicação da ISO
  • Novos lançamentos dos principais compiladores já implementam a maior parte dos recursos do C23, conectando as mudanças do livro não apenas a uma apresentação experimental, mas também a ambientes reais de uso
  • Mudanças de linguagem e biblioteca como _BitInt(N), nullptr, auto, typeof e constexpr foram amplamente refletidas, impactando também a forma de escrever código C existente
  • Para permitir testar isso imediatamente em plataformas existentes, foram adicionados um apêndice de transição e headers include temporários, mas o MEAP da Manning ainda está em andamento

Material gratuito e documentos do padrão

  • A edição C23 de Modern C pode ser baixada gratuitamente
  • Na página dedicada do livro, também é possível encontrar materiais relacionados
  • A edição revisada ajusta várias explicações, mas seu objetivo principal é refletir o novo padrão C, o C23
  • Entre os documentos com acesso público, o material mais próximo do conteúdo do novo padrão é o PDF N3220
  • Novos lançamentos dos principais compiladores já implementam a maior parte dos novos recursos trazidos pelo C23

Mudanças da linguagem C23 e suporte à transição

  • As mudanças relacionadas a inteiros ocupam uma parte importante
    • Adição do novo tipo de precisão em bits _BitInt(N)
    • Inclusão de novos headers da biblioteca C para aritmética com verificação de overflow e manipulação de bits
    • Reflexo da possibilidade de tipos de 128 bits em arquiteturas modernas
    • Inclui melhorias práticas no tipo enum
  • Outros novos conceitos do C23 também estão incluídos
    • A constante nullptr e o tipo associado a ela
    • Anotações sintáticas por meio de attributes
    • Ferramentas de programação genérica de tipos, incluindo auto e typeof
    • Inicialização padrão com {} aplicada também a arrays de comprimento variável
    • constexpr para constantes nomeadas de todos os tipos
  • O novo material também aborda expressões compostas, lambdas, “internacionalização” e até uma abordagem abrangente para falhas de programa
  • Foram adicionados um apêndice e headers include temporários para começar a usar C23 imediatamente em plataformas existentes
  • O MEAP da nova edição da Manning ainda está aberto

1 comentários

 
GN⁺ 2024-10-16
Opiniões no Hacker News
  • Ao contrário da ordem de armazenamento da minha máquina, little-endian, o formato em que a representação dos dígitos mais significativos vem primeiro é chamado de big-endian; dizer que ambos são comumente usados em processadores modernos parece um pouco exagerado, já que, fora o s390x, quase não sobrou nada
    Imagino que em breve apareçam comentários sobre as arquiteturas big-endian de nicho/obsoletas favoritas de todo mundo

    • Arm é bi-endian e continua vivo na maioria dos celulares
      Como em outro comentário mais votado, “moderno” não quer dizer necessariamente popular ou amplamente usado
    • POWER é bi-endian. O Linux on POWER recente é little-endian; no passado, Linux on POWER big-endian era comum, mas as distribuições fizeram a transição há alguns anos. Por outro lado, AIX e IBM i são big-endian
      AIX e IBM i talvez não sejam tão ativos quanto os mainframes da IBM, mas dá para dizer que o AIX está mais vivo até que Solaris ou HP/UX, ainda mais quando comparado aos muitos Unix comerciais de antigamente. O IBM i também está sobrevivendo por pouco, mas está muito mais vivo do que plataformas midrange legadas concorrentes cujo suporte oficial do fornecedor foi encerrado, como o HP MPE
    • MIPS ainda está bastante vivo em hardware de rede para consumidores
    • De certo modo, dá até para dizer que todos os processadores o usam com frequência. Isso porque a ordem de bytes de rede é big-endian
    • Sparc não é big-endian?
  • O aspecto mais importante de C é a portabilidade. O ponto central é ir de pequenos microcontroladores a quase todas as plataformas de computação, mas tenho dúvidas de que uma nova versão de C seja adotada nesse nível
    Se eu quisesse usar algo de ponta, acho que escolheria C++2x ou Rust em vez de C. Estou deixando passar algo? Fico curioso sobre quais são as vantagens desse tal C moderno

    • Uma das vantagens de escrever código em C é não precisar entrar em discussões cansativas sobre como deveria ser o código idiomático ou qual subconjunto da linguagem é o correto
      Se a ideia é estar na ponta, recomendo Zig. A complexidade da linguagem é muito menor que a de C++ moderno e Rust. Um bom efeito colateral menos chamativo do C23 é aproximar sintaxes como ... = {} e {0} das de C++, tornando menos incômodo para mantenedores de bibliotecas C dar suporte a pessoas que querem compilar código C com um compilador C++
    • Como aconteceu no passado com C11, extensões GNU e recursos individuais que agora entraram no C23, isso acabará sendo adotado. Por exemplo, a notação binária 0b é amplamente usada no mundo dos microcontroladores
      Toolchains de microcontroladores em geral são construídas em cima do GCC, então recebem recursos de graça. Ainda existem compiladores C proprietários que estão sempre atrasados, mas eles não são tão importantes quanto eram há 20 anos
    • Esses recursos acabarão chegando ao mainstream. É parecido com o que está acontecendo agora com o C11
      Se você não mira embarcados ou um conjunto muito amplo de arquiteturas, também não há motivo para não usar C23 a partir de hoje
    • O especificador thread_local já é usado em algumas plataformas de microcontroladores, mas antes do C11 era completamente ilegal. Ainda assim, ele simplifica muito a gestão de memória em ambientes com threads
      Há algum motivo para entrar no mundo C++ só por causa disso?
    • Se o alvo é suportado por LLVM/GCC, você não recebe automaticamente as novas versões de C também? Só não receberia em toolchains GCC antigas modificadas por fornecedores para outras arquiteturas
  • Pessoalmente, coisas como guard, defer, auto, constexpr e nullptr tornam C muito mais complexo. Eu escolho C porque preciso de simplicidade; se eu quisesse complexidade, normalmente escolheria C++, mesmo sem querer muito, ou então Go — ou Elixir, se fosse para servidor
    _BitInt(N) também é feio e me lembra _Bool, que felizmente agora virou bool. constexpr e nullptr têm cheiro forte demais de C++. Ainda assim, Modern C é um ótimo livro, e eu o tenho usado bem para C99, que pretendo continuar usando

    • A pergunta “qual é o problema com NULL?” é respondida lendo os documentos relacionados, graças a uma das poucas vantagens da padronização ISO: https://wg21.link/p2312
      Em resumo, passar um argumento NULL para macros genéricas por tipo pode gerar resultados surpreendentes, e o status de expressões condicionais como (1 ? 0 : NULL) e (1 ? 1 : NULL) muda dependendo de como NULL é definido. Além disso, passar NULL para uma função variádica que espera um ponteiro pode ter consequências sérias. Em muitas arquiteturas atuais, int e void* têm tamanhos diferentes; se NULL for simplesmente 0, um argumento do tamanho errado será passado à função
    • Acho que a complexidade não cresce de forma linear. Na verdade, muitas vezes ferramentas mais complexas tornam o processo e o resultado final mais simples
      É parecido com construir uma casa. Um martelo e uma chave de fenda são muito simples, e um guindaste é extremamente complexo, mas quem simplifica a construção da casa é o guindaste. Se você tivesse que construir uma casa só com martelo e chave de fenda, precisaria inventar um procedimento extremamente complexo
      Com linguagens de programação é a mesma coisa. Criar contêineres genéricos em C++ é trivial, mas em C é muito difícil. Dá para imitar até certo ponto com void * e casts manuais, mas é trabalhoso, propenso a erros e torna o código mais complexo
      O mesmo vale para std::sort e qsort. Com o poder de templates e objetos de função, a implementação fica mais simples e rápida. Não é preciso passar void * nem desreferenciar em tempo de execução, e a comparação pode ser colocada na própria definição da função. Não há chamada indireta, não há passagem pela pilha, e a função de comparação ainda pode ser inlinada. Complexidade da linguagem não significa complexidade de implementação
    • auto é útil principalmente quando se mexe com macros genéricas por tipo, mas é melhor não usá-lo em código comum. Espero que evitem aquela loucura de almost always auto que virou moda por um tempo no mundo C++
      Infelizmente, há pequenas diferenças entre compiladores. Pelo que lembro, Clang implementava o auto no estilo C++ e GCC implementava o auto no estilo C, então havia diferenças sutis com ponteiros auto. Não sei se essa diferença já foi corrigida
      _BitInt(N) normalmente não é usado diretamente; usa-se um typedef com a largura necessária. Por exemplo, typedef _BitInt(2) u2;. Sintaxes feias como _B são necessárias porque combinações de sublinhado seguido de letra maiúscula são reservadas pelo padrão C, para evitar conflito com código existente ao adicionar pequenos recursos à linguagem. O nome _Bool existe pelo mesmo motivo. Pelo que sei, defer na verdade não entrou no C23
  • A definição antiga nem sequer especificava se NULL era um ponteiro ou um inteiro. Por isso, em plataformas que não seguiam a exigência do POSIX de ((void*)0), era uma armadilha sob os pés, sem ser nem tipo de ponteiro nem tamanho de ponteiro
    constexpr e nullptr têm cheiro de C++ provavelmente porque foram reimportados do C++. Ainda assim, NULL continua podendo ser usado; por fora, parece ter sido redefinido como nullptr

    • Este código tem um bug e, em algumas arquiteturas, pode até causar crash: execlp("echo", "echo", "Hello, world!", NULL);
      Este código não tem esse bug: execlp("echo", "echo", "Hello, world!", nullptr);
      Isto também está ok: execlp("echo", "echo", "Hello, world!", (char *)NULL);
  • Eu ia perguntar se havia uma boa lista de livros de C, mas encontrei a resposta por conta própria. Aqui, Modern C é classificado como intermediário
    https://stackoverflow.com/questions/562303/the-definitive-c-...

    • Gosto de Modern C, e ele é bem avaliado em vários lugares. Concordo que é intermediário
      Como companheiros modernos para o C de K&R, considero 21st Century C, de Ben Klemens, e C Programming: A Modern Approach, de King, alternativas mais acessíveis
    • Fluent C: Principles, Practices and Patterns, de Christopher Preschern, também vale conferir
    • Essa lista não está completa. Por exemplo, falta Effective C: https://nostarch.com/effective-c-2nd-edition
      Pessoalmente, prefiro Effective C a Modern C. Modern C é tão rigoroso que parece ler uma especificação comentada da linguagem, algo que pode ser necessário para especialistas; para alguém como eu, que usa C de forma mais leve, é uma leitura entediante
  • Gosto bastante de algumas das extensões High C da Metaware
    https://news.ycombinator.com/item?id=41647843
    https://news.ycombinator.com/item?id=38938402

  • Há mais de um ano venho usando C++ moderno em um projeto pessoal, um interpretador de linguagem, mas por causa da carga mental do C++ e dos problemas de ferramental continuo pensando em migrar para C. O IntelliSense do Visual Studio ainda quase não funciona quando se usam módulos do C++20, e, por causa das falhas da linguagem, coisa demais acaba sendo empurrada para as interfaces, deixando os tempos de compilação horrorosos
    Por outro lado, já me acostumei tanto com classes, funções-membro, programação genérica e namespaces que talvez eu já tenha caído na armadilha

    • Uso C++ há muito tempo e não penso de jeito nenhum em abrir mão de destrutores para passar para C
      Para esse caso, você já considerou C#? O Visual Studio combina muito melhor com C#
  • No Preview do macOS, clicar nos links do sumário na barra lateral não funciona direito

    • Testei alguns links do sumário no leitor de PDF zathura e funcionaram bem
    • O sumário atual está claramente quebrado
  • Só faz alguns anos que, em uma biblioteca que mantenho, pude confiar que todos os compiladores C davam suporte a C99: https://github.com/eyalroz/printf
    Mas, alguns anos depois, como sempre, foi aberta uma issue pedindo compatibilidade com C89 por causa de alguma toolchain embarcada antiquíssima. Então C23 é legal, mas a sensação é de: vamos conversar de novo daqui a uns 20 anos

  • Alguém pode linkar um texto que explique, na prática, por que C está de fato parado no C99? Quase não vejo projetos dignos de menção usando recursos posteriores ao C11

    • C99 ainda é novo. A Microsoft tentou matar C ao se recusar a implementar qualquer coisa que também não existisse em C++. O MSVC atrasou 16 anos para implementar C99, e implementou só o mínimo. A implementação de C11 também chegou 11 anos atrasada
      Como C ficou praticamente congelado por décadas, a base de usuários parece ter se autosselecionado para pessoas que gostam de C como ele é e não se importam em dar suporte a compiladores velhos e cheios de tralhas. Quem perdeu a paciência ou queria uma linguagem do século XXI foi para C++/Rust/Zig etc.
    • A Microsoft basicamente atrapalhou o C99 ao quase não implementar recursos de C99 no compilador C do Visual Studio até por volta de 2015. E só em 2019 admitiu o fracasso e voltou a dar suporte a versões mais recentes de C. O front-end C do MSVC ainda fica consistentemente atrás de Clang e GCC
      Por volta de 2010, o MSVC ainda era muito importante, o que soa estranho do ponto de vista atual, em que parece que a maioria dos desenvolvedores migrou para Linux. Por outro lado, também não há muitos projetos que realmente precisem de recursos do C11. O C11 também tirou de C99 os VLAs, o que não foi uma perda tão lamentável. C23 talvez seja a primeira versão desde C99 para a qual muitas codebases em C possam realmente valer a pena fazer upgrade