Incluindo um arquivo de origem C em outro?

111

É correto (ou mesmo recomendado / boa prática) para #includeum .carquivo em outro .carquivo?

SS Anne
fonte
4
Tomei a liberdade de editar a pergunta e também apliquei algumas tags úteis. Algumas das minhas tags podem mostrar um viés, heh.
relaxe em

Respostas:

113

Usado corretamente, pode ser uma técnica útil.

Digamos que você tenha um subsistema complexo e crítico para o desempenho com uma interface pública relativamente pequena e muitos códigos de implementação não reutilizáveis. O código tem vários milhares de linhas, uma centena de funções privadas e muitos dados privados. Se você trabalha com sistemas embarcados não triviais, provavelmente lida com essa situação com frequência suficiente.

Sua solução provavelmente será em camadas, modular e desacoplada e esses aspectos podem ser representados e reforçados de forma útil pela codificação de diferentes partes do subsistema em arquivos diferentes.

Com C, você pode perder muito fazendo isso. Quase todos os conjuntos de ferramentas fornecem otimização decente para uma única unidade de compilação, mas são muito pessimistas sobre qualquer coisa declarada externa.

Se você colocar tudo em um módulo de origem C, você obtém -

  • Melhorias no desempenho e no tamanho do código - as chamadas de função serão incorporadas em muitos casos. Mesmo sem inlining, o compilador tem oportunidades de produzir código mais eficiente.

  • Dados de nível de link e ocultação de função.

  • Evitar a poluição do namespace e seu corolário - você pode usar nomes menos pesados.

  • Compilação e ligação mais rápidas.

Mas você também obtém uma bagunça profana quando se trata de editar este arquivo e você perde a modularidade implícita. Isso pode ser superado dividindo a fonte em vários arquivos e incluindo-os para produzir uma única unidade de compilação.

Você precisa impor algumas convenções para gerenciar isso de forma adequada. Isso vai depender de seu conjunto de ferramentas até certo ponto, mas algumas dicas gerais são -

  • Coloque a interface pública em um arquivo de cabeçalho separado - você deve fazer isso de qualquer maneira.

  • Tenha um arquivo .c principal que inclua todos os arquivos .c subsidiários. Isso também pode incluir o código para a interface pública.

  • Use protetores de compilador para garantir que cabeçalhos privados e módulos de origem não sejam incluídos por unidades de compilação externas.

  • Todos os dados e funções privados devem ser declarados estáticos.

  • Mantenha a distinção conceitual entre os arquivos .c e .h. Isso aproveita as convenções existentes. A diferença é que você terá muitas declarações estáticas em seus cabeçalhos.

  • Se seu conjunto de ferramentas não impõe nenhuma razão para não fazê-lo, nomeie os arquivos de implementação privados como .c e .h. Se você usar proteções de inclusão, elas não produzirão nenhum código e não introduzirão novos nomes (você pode acabar com alguns segmentos vazios durante a ligação). A grande vantagem é que outras ferramentas (por exemplo, IDEs) tratarão esses arquivos de maneira adequada.


fonte
1
+1 esta ainda é a realidade, enquanto melhores compiladores tornarão este método obsoleto com o tempo. O GCC 4.5 com otimização de tempo de link é um grande passo no caminho.
u0b34a0f6ae
Já vi tantos programas fazerem isso e fico nervoso quando tento reutilizar seu código. Obrigado por explicar por que eles fazem isso e sugerir o uso de um guarda (o que geralmente não é feito).
Joey Adams
No desenvolvimento embarcado para C51, o uso de arquivos C externos e múltiplos causou nada além de dores de cabeça. Estou mudando para ter um arquivo C incluindo todos os outros para voltar meu tempo.
mahesh
Para os registros: GCC oferece suporte à otimização em diferentes unidades de tradução, consulte este tópico do SO e a seção do manual do GCC sobre otimização de tempo de link .
Twonky
60

esta ok? sim vai compilar

é recomendado? não - os arquivos .c são compilados em arquivos .obj, que são vinculados após a compilação (pelo vinculador) no executável (ou biblioteca), portanto, não há necessidade de incluir um arquivo .c em outro. O que você provavelmente deseja fazer é criar um arquivo .h que liste as funções / variáveis ​​disponíveis no outro arquivo .c e incluir o arquivo .h

Steven A. Lowe
fonte
14
Também vale a pena notar que, mesmo se compilar, pode não ser vinculado se o arquivo #included .c também for compilado e os dois arquivos de objeto estiverem vinculados - você pode acabar com vários símbolos definidos.
Nick Meyer
1
Uma pequena pergunta. Eu tenho um arquivo de cabeçalho declarando um método struct + e também o arquivo .c correspondente para defini-los. Se esse método usa a estrutura como parâmetro, como evitaria incluir o arquivo .c em outro arquivo .c onde o método principal é definido?
stdout
12

Não.

Dependendo do seu ambiente de construção (você não especifica), você pode descobrir que ele funciona exatamente da maneira que você deseja.

No entanto, existem muitos ambientes (ambos IDEs e muitos Makefiles feitos à mão) que esperam compilar * .c - se isso acontecer, você provavelmente acabará com erros de linker devido a símbolos duplicados.

Como regra, esta prática deve ser evitada.

Se você absolutamente deve # incluir o código-fonte (e geralmente deve ser evitado), use um sufixo de arquivo diferente para o arquivo.

Andrew Edgecombe
fonte
9

Pensei em compartilhar uma situação em que minha equipe decidiu incluir arquivos .c. Nossa arquitetura consiste basicamente em módulos que são desacoplados por meio de um sistema de mensagens. Esses manipuladores de mensagens são públicos e chamam muitas funções de trabalho estáticas locais para fazer seu trabalho. O problema surgiu ao tentar obter cobertura para nossos casos de teste de unidade, já que a única maneira de exercitar esse código de implementação privado era indiretamente por meio da interface de mensagem pública. Com algumas funções de trabalhador até os joelhos na pilha, isso acabou sendo um pesadelo para conseguir uma cobertura adequada.

Incluir os arquivos .c nos deu uma maneira de alcançar a engrenagem da máquina que estávamos testando.

Matthew Alford
fonte
6

Você pode usar o compilador gcc no linux para vincular dois arquivos c em uma saída. Suponha que você tenha dois arquivos c, um é 'main.c' e o outro é 'support.c'. Portanto, o comando para vincular esses dois é

gcc main.c support.c -o main.out

Por isso, dois arquivos serão ligados a uma única saída main.out Para executar a saída, o comando será

./main.out

Se você estiver usando a função em main.c, que é declarada no arquivo support.c, você deve declará-la em main também usando a classe de armazenamento externo.

Sumitroy
fonte
5

A extensão do arquivo não importa para a maioria dos compiladores C, então funcionará.

No entanto, dependendo do makefile ou das configurações do projeto, o arquivo c incluído pode gerar um arquivo de objeto separado. Ao vincular, isso pode levar a símbolos de definição dupla.

HS.
fonte
3

Você pode incluir corretamente os arquivos .C ou .CPP em outros arquivos de origem. Dependendo do seu IDE, você geralmente pode evitar a ligação dupla olhando as propriedades dos arquivos de origem que deseja incluir, geralmente clicando com o botão direito e clicando em propriedades, e desmarcar / marcar compilar / vincular / excluir da construção ou qualquer outra opção talvez. Ou você não poderia incluir o arquivo no próprio projeto, assim o IDE nem saberá que ele existe e não tentará compilá-lo. E com makefiles você simplesmente não colocaria o arquivo nele para compilar e vincular.

EDIT: Desculpe, eu fiz uma resposta em vez de uma resposta para outras respostas :(

MikeyPro
fonte
1

A linguagem C não proíbe esse tipo de #include, mas a unidade de tradução resultante ainda precisa ser C. válida

Não sei qual programa você está usando com um arquivo .prj. Se você estiver usando algo como "make" ou Visual Studio ou qualquer outra coisa, apenas certifique-se de definir sua lista de arquivos a serem compilados sem aquele que não pode ser compilado independentemente.

Programador Windows
fonte
0

Incluir o arquivo C em outro arquivo é legal, mas não é aconselhável, a menos que você saiba exatamente por que está fazendo isso e o que está tentando alcançar.
Tenho quase certeza que se você postar aqui o motivo pelo qual por trás de sua pergunta a comunidade encontrará para você outra forma mais adequada de atingir seu objetivo (observe o "quase", pois é possível que essa seja a solução dado o contexto )

A propósito, perdi a segunda parte da pergunta. Se o arquivo C for incluído em outro arquivo e ao mesmo tempo incluído no projeto, você provavelmente acabará com o problema de símbolo duplicado porque vincular os objetos, ou seja, a mesma função será definida duas vezes (a menos que sejam todos estáticos).

Ilya
fonte
-6

você deve adicionar um cabeçalho como este

#include <another.c>

nota: ambos os arquivos devem ser colocados no mesmo lugar

Eu uso isso no codevison AVR para micro controladores ATMEGA e funciona de verdade, mas não funciona no arquivo de linguagem C normal

milad
fonte