
Resumindo: porque assim dissemos.
:)
Ok, essa é uma explicação muito curta para um artigo, caro leitor, e minhas palavras provocativas exigem uma explicação.
A reunião do comitê de linguagem C - que foi planejada originalmente para ser realizada em Freiburg, Alemanha, mas não cresceu junto por razões óbvias - terminou em 7 de agosto. Correu bem, avançámos em todas as frentes. Sim, estamos realmente fazendo progressos, garanto-lhe, e C não está morto.
Também mencionarei que me tornei o Editor do projeto C, então antes de tomar o título como uma declaração ignorante de alguém que é muito preguiçoso para "tentar melhorar", quero garantir a vocês que estou realmente trabalhando muito para garantir que C possa para satisfazer as necessidades dos desenvolvedores sem ter que parafusar 50 extensões específicas para construir bibliotecas e aplicativos mais ou menos bonitos e úteis.
Mesmo assim, eu disse isso (que a linguagem C nunca o impedirá de cometer erros), o que significa que preciso justificar. Podemos olhar para milhares de CVEs e tickets associados com um monte de código C, ou podemos fazer com que o MISRA verifique vigorosamente cada recurso C para uso indevido em potencial ( olá, declarações de protótipo K&R...) ou ter bugs mais complexos e engraçados relacionados à portabilidade e comportamento indefinido. Mas, em vez disso, lemos a fonte original - o que o próprio Comitê disse.
Oh, é hora de pegar um pouco de pipoca ?!
Não caro leitor, coloque a pipoca de lado. Como acontece com todos os procedimentos ISO, não posso citar as palavras de ninguém, e este artigo não pretende envergonhar ninguém. Mas vou explicar por que algo que podemos facilmente considerar mau comportamento em um documento ISO C compatível com os padrões nunca será descartado. E vamos começar com o documento do Dr. Philipp Klaus Krause:
N2526, use const para dados da biblioteca que não serão modificados .
N2526 é um documento muito simples.
, , , . — , ! …
Concordo, não é exatamente a mesma coisa, mas tenho certeza de que a ideia lhe parecerá razoável, caro leitor. Quando este documento foi posto a votação, quase não houve votos contra. Mais tarde, várias pessoas objetaram veementemente porque essa proposta violava o código antigo. Claro, isso é ruim: até eu recupero o fôlego quando penso em adicionar
const
? Não há ABI na linguagem C que pode ser influenciada pela inovação. C (sua implementação) nem dá atenção aos qualificadores, como podemos quebrar alguma coisa ?! Vamos ver por que algumas pessoas acham que essa será uma mudança significativa.
Linguagem C
Ou, como gosto de chamá-lo, "segurança de digitação é para idiomas falhos". Sim, muito prolixo, então vamos parar em "C". Você pode estar se perguntando por que digo que linguagens como C não são seguras para tipos. Afinal, aqui está:
struct Meow {
int a;
};
struct Bark {
double b;
void* c;
};
int main (int argc, char* argv[]) {
(void)argc;
(void)argv;
struct Meow cat;
struct Bark dog = cat;
// error: initializing 'struct Bark' with an expression of incompatible type 'struct Meow'
return 0;
}
Para ser honesto, isso me parece um tipo de segurança forte, Jim! E assim tudo se torna ainda mais picante:
#include <stdlib.h>
struct Meow {
int a;
};
struct Bark {
double b;
void* c;
};
int main (int argc, char* argv[]) {
(void)argc;
(void)argv;
struct Meow* p_cat = (struct Meow*)malloc(sizeof(struct Meow));
struct Bark* p_dog = p_cat;
// :3
return 0;
}
Sim, o padrão C permite que dois tipos de ponteiros completamente independentes se refiram um ao outro. A maioria dos compiladores irá avisá-lo sobre isso, mas o padrão exige que você aceite este código, a menos que você o desenrole
-Werror
-Wall
-Wpedantic
, etc., etc., etc.
Na verdade, o compilador pode aceitar isso sem conversão explícita:
volatile
(quem precisa dessa semântica?!)const
(escreva todos os dados somente leitura aqui!)_Atomic
(thread de segurança!)
Não estou dizendo que você não deva ser capaz de fazer tudo isso. Mas quando você escreve em C - no qual é muito fácil criar uma função de 500-1000 linhas com nomes de variáveis completamente incompreensíveis - Infa Sotka, você trabalha principalmente com ponteiros, e geralmente falta segurança em termos de linguagem base. Nota: isso viola as restrições, mas tanto código antigo já foi escrito que toda implementação de alguma forma ignora os qualificadores, e por causa disso, seu código não será impedido de compilar ( obrigado @fanf!)! Nessa situação, é possível com o compilador identificar facilmente todas as falhas em potencial e você receberá avisos, mas não será obrigado a fazer o typecast para permitir que o compilador saiba o que você realmente deseja fazer. Mais importante, porém, os seres humanos que virão depois de você também não entenderão o que você se propôs a fazer.
Tudo que você precisa fazer é remover o recurso
-Werror
-Wall
-Wpedantic
, e você estará pronto para cometer os crimes de multithreading, modo somente leitura e registros de hardware.
É tudo justo agora, certo? Se alguém remover todos esses sinalizadores de aviso e erro, eles não se importarão que tipo de erros ou coisas estúpidas você faça. Isso significa que, no final, esses avisos são completamente irrelevantes e inofensivos no que diz respeito à conformidade com ISO C. E ainda ...
Estamos considerando quebrar avisos
Sim.
Este é um inferno especial ao qual os desenvolvedores C e, em menor medida, os desenvolvedores C ++ estão acostumados. Os avisos são irritantes e, como mostra a prática, incluindo
-Weverything
ou /W4
muito irritantes. Escondendo avisos sobre variáveis no namespace global (obrigado, agora todos os cabeçalhos e bibliotecas C são um problema), usando nomes "reservados" (como as crianças dizem, " lol nice one xd !! "), e também "esta estrutura tem preenchimento porque você usou alignof
"(... sim, sim, eu sei que ela tem preenchimento, eu pedi explicitamente mais preenchimento, PORQUE EU USEI alignof
, SR. COMPILADOR) - tudo isso leva muito tempo.
Mas esses são avisos.
Mesmo que sejam irritantes, ajudam a evitar problemas. O fato de que posso ignorar descaradamente todos os qualificadores e negligenciar todos os tipos de segurança de leitura, gravação, transmissão e somente leitura é uma grande preocupação quando se trata de comunicar minhas intenções e evitar bugs. Mesmo a antiga sintaxe K&R gerava bugs nas bases de código industriais e governamentais porque os usuários estavam fazendo algo errado. E eles fizeram isso não porque fossem péssimos programadores, mas porque trabalham com bases de código que geralmente são mais antigas do que eles e estão se preparando para lutar contra eles. longos milhões de linhas. É impossível manter a base de código inteira em sua cabeça: convenções, análise estática, avisos de alto nível e outras ferramentas são para isso. Infelizmente,
todo mundo quer ter um código sem avisos.
Isso significa que quando o desenvolvedor do GCC torna os avisos mais sensíveis a situações potencialmente problemáticas, os mantenedores (não os desenvolvedores originais) inesperadamente recebem logs em negrito de vários gigabytes do código antigo, contendo muitos novos avisos e todos os tipos de coisas diferentes. "Isso é idiotice", dizem eles, "o código funcionou por ANOS, então por que o GCC está reclamando agora?" Ou seja, mesmo que você adicione
const
funções à assinatura, mesmo que seja moral, espiritual e realmente correta, ela será evitada. "Romper" as pessoas significa "agora elas precisam procurar um código com intenções duvidosas". Este é um código que pode - sob pena de comportamento indefinido - destruir seu chip ou danificar sua memória.... Mas esse é outro problema que acompanha a profissão de desenvolvedor C atualmente.
Idade como medida de qualidade
Quantas pessoas presumiram que
sudo
tinham uma vulnerabilidade primitiva como "-1 ou um estouro de número inteiro dá acesso a tudo"? Quantas pessoas pensaram que Heartbleed poderia ser um problema real? Quantos desenvolvedores de jogos enviam "pequenas" bibliotecas stb sem nem mesmo alterá-las e não perceber que essas bibliotecas contêm vulnerabilidades de entrada mais importantes do que você pode imaginar? Não estou criticando todos esses desenvolvimentos ou seus autores: eles nos fornecem uma ajuda vital, da qual o mundo dependeu por décadas, muitas vezes com pouco ou nenhum apoio até que surja algum grande problema. Mas as pessoas que idolatram esses desenvolvimentos e os colocam por conta própria, então exalam um ditado venenoso de si mesmas, ilustrando o erro do sobrevivente:
, ?
Mantendo os princípios de compatibilidade retroativa e "relaxado" como os mais elevados ideais de C, as pessoas que sobrevivem por tempo suficiente nesta indústria começam a equiparar idade com qualidade, como bases de código são barris de vinho. Quanto mais antigo e mais longo for o código, mais fino e refinado será o vinho.
Infelizmente, nem tudo é tão romântico e fofo: cheio de bugs, com abundância de brechas de segurança, toda essa dívida técnica fica mais perigosa a cada dia. Com o tempo, todos os sistemas se transformam em montes apodrecidos de meia-vida, mal cuidados e parcialmente sem suporte. Eles são embelezados e recebem um espírito de nobreza, mas na realidade são múmias que estão apenas esperando para serem cutucadas desajeitadamente e, em seguida, seus furúnculos antediluvianos infeccionados explodirão e inundarão seu aplicativo com seu lindo botulismo experiente.
Hmm ... nojento. Mas e quanto ao Padrão C?
O problema que notei durante minha (incrivelmente curta) gestão como participante de uma reunião é que colocamos a compatibilidade com versões anteriores em primeiro plano. Para aqueles que estão migrando para o C hoje, estamos nos agarrando a aplicativos antigos e seus casos de uso e nos privando da oportunidade de melhorar a proteção, a segurança ou a arte do código C. , ele pode desligá-los. Esses avisos, e não erros, não são em vão: uma máquina C abstrata não requerpara fazer diagnósticos, o ISO C permite que esse código seja aceito em modos de montagem estritos. E isso ajudaria o mundo inteiro a romper com as APIs que afirmam abertamente, "mudar o conteúdo que fornecemos a você é um comportamento indefinido".
No entanto, mudamos de ideia sobre este documento depois que o motivo foi dado como “não podemos introduzir novos avisos”.
O argumento contra a proposta era: "Há muito código escrito que será quebrado se mudarmos essas assinaturas." Isso, novamente, limita as alterações aos avisos em termos de comportamento de quebra (lembre-se, conversões implícitas que removem qualificadores - até mesmo
_Atomic
- totalmente válidos de acordo com a ISO C, mesmo que violem as restrições). Se fosse esse o caso, cada autor de compilador introduziria algo como Ages in Rust, For Warnings Only, para dar às pessoas um benchmark "estável" para teste. Esta proposta não é nova: li documentos semelhantes dos engenheiros da Coverity sobre como gerar novos alertas e como os usuários reagem a eles. É difícil gerenciar a "confiança" dos desenvolvedores sobre novos avisos e outras coisas. Demora muito para convencer as pessoas de sua utilidade. Até mesmo John Carmack teve que trabalhar duro para obter o conjunto certo de avisos e erros de suas ferramentas de análise estática para se adequar ao seu desenvolvimento, antes de concluir que era "irresponsável não usar isso" .
Mesmo assim, nós, o Comitê, não concordamos em adicionar
const
funções de retorno de valor às quatro funções, porque isso adicionaria avisos a códigos potencialmente perigosos. Objetivamos que a antiga sintaxe K&R fosse descontinuada, apesar das fortes evidências de descuidos inocentes e vulnerabilidades graves na passagem dos tipos errados. Nós quase acrescentou comportamento indefinido para o pré-processador, apenas para obtê-lo para baixo, mas para fazer a implementação C "se comportar como deveria". Devido à compatibilidade com versões anteriores, sempre andamos no limite para evitar cometer erros óbvios. E isso, caro leitor, é o que mais me assusta sobre o futuro de S.
Padrão C não protege você
Não se engane: não importa o que os programadores digam ou sussurram para você. O comitê gestor da linguagem C é muito claro. Não adicionaremos novos avisos ao seu código antigo, mesmo que esse código possa ser perigoso. Não o impediremos de cometer erros, porque isso pode minar a ideia de como seu código antigo funciona, o que está errado. Não ajudaremos novatos a escrever um código C. Não exigiremos que seu código antigo esteja em conformidade com nenhum padrão. Cada novo recurso será opcional porque não podemos imaginar forçar os autores do compilador a seguir um padrão mais alto ou esperar mais de nossos desenvolvedores de bibliotecas padrão.
Vamos deixar o compilador mentir para você. Vamos mentir para o seu código. E quando tudo der errado - haverá um erro, "Oh, algum tipo de lixo aconteceu", haverá um vazamento de dados - nós balançaremos nossas cabeças solenemente. Vamos compartilhar nossas idéias e orar por você e dizer: "Bem, que vergonha." De fato, vergonha ...
Talvez um dia possamos consertar, caro leitor.