Por exemplo, a ferramenta SysInternals "FileMon" do passado possui um driver no modo kernel cujo código fonte está inteiramente em um arquivo de 4.000 linhas. O mesmo para o primeiro programa de ping já escrito (~ 2.000 LOC).
Por exemplo, a ferramenta SysInternals "FileMon" do passado possui um driver no modo kernel cujo código fonte está inteiramente em um arquivo de 4.000 linhas. O mesmo para o primeiro programa de ping já escrito (~ 2.000 LOC).
Respostas:
O uso de vários arquivos sempre exige sobrecarga administrativa adicional. É necessário configurar um script de construção e / ou arquivo de criação com estágios separados de compilação e vinculação, garantir que as dependências entre os diferentes arquivos sejam gerenciados corretamente, escrever um script "zip" para facilitar a distribuição do código-fonte por email ou download, e assim em. Hoje em dia, os IDEs modernos normalmente exigem muito desse trabalho, mas tenho certeza de que, no momento em que o primeiro programa de ping foi gravado, esse IDE não estava disponível. E para arquivos tão pequenos quanto ~ 4000 LOC, sem um IDE que gere vários arquivos para você, a troca entre a sobrecarga mencionada e os benefícios do uso de vários arquivos pode permitir que as pessoas tomem uma decisão pela abordagem de arquivo único.
Porque C não é bom em modularização. Fica confuso (arquivos de cabeçalho e #includes, funções externas, erros de tempo de link, etc.) e quanto mais módulos você inserir, mais complicado será.
As linguagens mais modernas têm melhores recursos de modularização em parte porque aprenderam com os erros de C e facilitam a decomposição da base de código em unidades menores e mais simples. Mas com C, pode ser benéfico evitar ou minimizar todo esse problema, mesmo que isso signifique agrupar o que de outra forma seria considerado muito código em um único arquivo.
Além dos motivos históricos, há um motivo para usá-lo em softwares modernos sensíveis ao desempenho. Quando todo o código está em uma unidade de compilação, o compilador pode executar otimizações de todo o programa. Com unidades de compilação separadas, o compilador não pode otimizar o programa inteiro de determinadas maneiras (por exemplo, embutir determinado código).
O vinculador certamente pode executar algumas otimizações além do que o compilador pode fazer, mas não todas. Por exemplo: vinculadores modernos são realmente bons em eliminar funções não referenciadas, mesmo em vários arquivos de objetos. Eles podem executar outras otimizações, mas nada como o que um compilador pode fazer dentro de uma função.
Um exemplo bem conhecido de um módulo de código de fonte única é o SQLite. Você pode ler mais sobre isso na página Amalgamação SQLite .
1. Sumário Executivo
Mais de 100 arquivos de origem separados são concatenados em um único arquivo grande de código C chamado "sqlite3.c" e chamado "a fusão". A fusão contém tudo o que um aplicativo precisa para incorporar o SQLite. O arquivo de amálgama tem mais de 180.000 linhas e mais de 6 megabytes de tamanho.
A combinação de todo o código do SQLite em um arquivo grande facilita a implantação do SQLite - há apenas um arquivo para acompanhar. E como todo o código está em uma única unidade de tradução, os compiladores podem otimizar melhor a inter-procedimentos, resultando em um código de máquina entre 5% e 10% mais rápido.
$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(CFILES)
do que mover tudo para um único arquivo de som. Você pode até fazer a compilação do programa inteiro como um destino alternativo ao script de construção tradicional que ignora a recompilação de arquivos de origem que não foram alterados, semelhante à maneira como as pessoas podem desativar a criação de perfil e a depuração para o destino de produção. Você não tem essa opção se tudo estiver em uma grande fonte de heap. Não é o que as pessoas estão acostumadas, mas não há nada complicado nisso.
Além do fator de simplicidade mencionado pelo outro entrevistado, muitos programas em C são escritos por um indivíduo.
Quando você tem uma equipe de indivíduos, torna-se desejável dividir o aplicativo entre vários arquivos de origem para evitar conflitos gratuitos nas alterações de código. Especialmente quando há programadores avançados e muito juniores trabalhando no projeto.
Quando uma pessoa está trabalhando sozinha, isso não é um problema.
Pessoalmente, eu uso vários arquivos com base na função como uma coisa habitual. Mas sou só eu.
Porque C89 não tinha inline
funções. O que significava que dividir seu arquivo em funções causava a sobrecarga de empurrar valores na pilha e saltar. Isso adicionou um pouco de sobrecarga ao implementar o código em 1 instrução de chave grande (loop de evento). Mas um loop de eventos é sempre muito mais difícil de implementar com eficiência (ou mesmo corretamente) do que uma solução mais modularizada. Portanto, para projetos de grande porte, as pessoas ainda optariam por modularizar. Mas quando eles tinham o design pensado com antecedência e podiam controlar o estado em uma declaração de switch, eles optaram por isso.
Atualmente, mesmo em C, não é preciso sacrificar o desempenho para modularizar, porque mesmo em funções C podem ser incorporadas.
inline
palavra-chave nos compiladores C89, não era possível incorporar, e é por isso que você teve que escrever tudo em uma função gigante que está incorreta. Você nunca deve usar inline
como otimizações de desempenho - o compilador geralmente sabe melhor do que você (e pode ignorar a palavra-chave).
inline
palavra-chave possui semântica relacionada ao vinculador, que é mais importante do que a questão de executar ou não otimizações em linha, mas algumas implementações têm outras diretrizes para controlar o alinhamento interno e essas coisas às vezes podem ser muito importantes. Em alguns casos, uma função pode parecer muito grande para valer a pena, mas a dobragem constante pode reduzir o tamanho e o tempo de execução para quase nada. Um compilador que não recebe uma cutucada forte para incentivar o alinhamento
Isso conta como um exemplo de evolução, que me surpreende ainda não ter sido mencionado.
Nos dias sombrios da programação, a compilação de um único arquivo pode levar alguns minutos. Se um programa fosse modularizado, a inclusão dos arquivos de cabeçalho necessários (sem opções de cabeçalho pré-compilados) seria uma causa adicional significativa de desaceleração. Além disso, o compilador pode escolher / precisar manter algumas informações no próprio disco, provavelmente sem o benefício de um arquivo de troca automática.
Os hábitos que esses fatores ambientais levaram a transitar para práticas de desenvolvimento contínuas e se adaptaram lentamente ao longo do tempo.
Naquele momento, o ganho do uso de um único arquivo seria semelhante ao obtido pelo uso de SSDs em vez de HDDs.