Pergunta

Estou acostumado a fazer toda a minha codificação em um arquivo C.No entanto, estou trabalhando em um projeto grande o suficiente para que se torne impraticável fazê-lo.Eu os #incluí juntos, mas já encontrei casos em que #incluí alguns arquivos várias vezes, etc.Já ouvi falar de arquivos .h, mas não tenho certeza de qual é sua função (ou por que ter 2 arquivos é melhor que 1).

Que estratégias devo usar para organizar meu código?É possível separar funções "públicas" das "privadas" para um arquivo específico?

Esse pergunta precipitou minha investigação.O arquivo tea.h não faz referência ao arquivo tea.c.O compilador "sabe" que todo arquivo .h possui um arquivo .c correspondente?

Foi útil?

Solução

Você deve considerar os arquivos .h como arquivos de interface do seu arquivo .c.Cada arquivo .c representa um módulo com uma certa funcionalidade.Se as funções em um arquivo .c forem usadas por outros módulos (ou seja,outros arquivos .c) coloque o protótipo da função no arquivo de interface .h.Ao incluir o arquivo de interface em seu arquivo .c de módulos originais e em todos os outros arquivos .c nos quais você precisa da função, você disponibiliza essa função para outros módulos.

Se você precisar apenas de uma função em um determinado arquivo .c (não em qualquer outro módulo), declare seu escopo como estático.Isso significa que ele só pode ser chamado de dentro do arquivo c no qual está definido.

O mesmo vale para variáveis ​​usadas em vários módulos.Eles devem ir no arquivo de cabeçalho e lá devem ser marcados com a palavra-chave 'extern'.Observação:Para funções a palavra-chave 'extern' é opcional.As funções são sempre consideradas 'externas'.

As proteções de inclusão nos arquivos de cabeçalho ajudam a não incluir o mesmo arquivo de cabeçalho várias vezes.

Por exemplo:

Módulo1.c:

    #include "Module1.h"

    static void MyLocalFunction(void);
    static unsigned int MyLocalVariable;    
    unsigned int MyExternVariable;

    void MyExternFunction(void)
    {
        MyLocalVariable = 1u;       

        /* Do something */

        MyLocalFunction();
    }

    static void MyLocalFunction(void)
    {
      /* Do something */

      MyExternVariable = 2u;
    }

Módulo1.h:

    #ifndef __MODULE1.H
    #define __MODULE1.H

    extern unsigned int MyExternVariable;

    void MyExternFunction(void);      

    #endif

Módulo2.c

    #include "Module.1.h"

    static void MyLocalFunction(void);

    static void MyLocalFunction(void)
    {
      MyExternVariable = 1u;
      MyExternFunction();
    }

Outras dicas

Tente fazer com que cada .c se concentre em uma área específica de funcionalidade.Use o arquivo .h correspondente para declarar essas funções.

Cada arquivo .h deve ter uma proteção de 'cabeçalho' em torno de seu conteúdo.Por exemplo:

#ifndef ACCOUNTS_H
#define ACCOUNTS_H
....
#endif

Dessa forma, você pode incluir "accounts.h" quantas vezes quiser, e a primeira vez que for visto em uma unidade de compilação específica será a única que realmente extrairá seu conteúdo.

Compilador

Você pode ver um exemplo de um 'módulo' C em Este tópico - Observe que existem dois arquivos - o cabeçalho tea.h e o código tea.c.Você declara todas as definições públicas, variáveis ​​e protótipos de funções que deseja que outros programas acessem no cabeçalho.No seu projeto principal você #include e esse código agora pode acessar as funções e variáveis ​​do módulo tea mencionadas no cabeçalho.

Fica um pouco mais complexo depois disso.Se você estiver usando o Visual Studio e muitos outros IDEs que gerenciam sua construção para você, ignore esta parte - eles cuidam da compilação e vinculação de objetos.

Vinculador

Quando você compila dois arquivos C separados, o compilador produz arquivos de objetos individuais - então main.c se torna main.o e tea.c se torna tea.o.O trabalho do vinculador é examinar todos os arquivos de objeto (seu main.o e tea.o) e combinar as referências - então, quando você chama uma função tea em main, o vinculador modifica essa chamada para que realmente chame a função correta função no chá.O vinculador produz o arquivo executável.

Existe um ótimo tutorial que se aprofunda neste assunto, incluindo o escopo e outros problemas que você encontrará.

Boa sorte!

-Adão

Algumas regras simples para começar:

  1. Coloque as declarações que você deseja tornar "públicas" no arquivo de cabeçalho do arquivo de implementação C que você está criando.
  2. Somente #include arquivos de cabeçalho no arquivo C necessários para implementar o arquivo C.
  3. inclua arquivos de cabeçalho em um arquivo de cabeçalho somente se necessário para as declarações nesse arquivo de cabeçalho.

  4. Use o método include guard descrito por Andrew OU use #pragma uma vez se o compilador suportar (o que faz a mesma coisa - às vezes com mais eficiência)

Para responder à sua pergunta adicional:

Essepergunta precipitou minha investigação.O arquivo tea.h não faz referência ao arquivo tea.c.O compilador "sabe" que todo arquivo .h possui um arquivo .c correspondente?

O compilador não se preocupa principalmente com arquivos de cabeçalho.Cada chamada do compilador compila um arquivo fonte (.c) em um arquivo objeto (.o).Nos bastidores (ou seja,no make arquivo ou arquivo de projeto) uma linha de comando equivalente a esta está sendo gerada:

compiler --options tea.c

O arquivo de origem #includeSão todos os arquivos de cabeçalho dos recursos aos quais ele faz referência, que é como o compilador encontra os arquivos de cabeçalho.

(Estou encobrindo alguns detalhes aqui.Há muito o que aprender sobre a construção de projetos C.)

Além das respostas fornecidas acima, uma pequena vantagem de dividir seu código em módulos (arquivos separados) é que, se você precisar de variáveis ​​globais, poderá limitar seu escopo a um único módulo usando a palavra-chave ' estático'.(Você também pode aplicar isso a funções).Observe que esse uso de 'estático' é diferente de seu uso dentro de uma função.

Sua pergunta deixa claro que você realmente não fez um desenvolvimento muito sério.O caso normal é que seu código geralmente seja grande demais para caber em um arquivo.Uma boa regra é que você deve dividir a funcionalidade em unidades lógicas (arquivos .c) e cada arquivo não deve conter mais do que você pode facilmente manter em sua cabeça de uma só vez.

Um determinado produto de software geralmente inclui a saída de muitos arquivos .c diferentes.Como isso normalmente é feito é que o compilador produz vários arquivos objeto (em arquivos ".o" de sistemas unix, o VC gera arquivos .obj).O objetivo do "vinculador" é compor esses arquivos-objeto na saída (seja uma biblioteca compartilhada ou um executável).

Geralmente seus arquivos de implementação (.c) contêm código executável real, enquanto os arquivos de cabeçalho (.h) possuem as declarações das funções públicas nesses arquivos de implementação.Você pode facilmente ter mais arquivos de cabeçalho do que arquivos de implementação e, às vezes, os arquivos de cabeçalho também podem conter código embutido.

Geralmente é bastante incomum que os arquivos de implementação incluam uns aos outros.Uma boa prática é garantir que cada arquivo de implementação separe suas preocupações dos outros arquivos.

Eu recomendo que você baixe e veja a fonte do kernel do Linux.É bastante grande para um programa C, mas bem organizado em áreas separadas de funcionalidade.

Os arquivos .h devem ser usados ​​para definir os protótipos de suas funções.Isso é necessário para que você possa incluir os protótipos necessários em seu arquivo C sem declarar todas as funções necessárias em um único arquivo.

Por exemplo, quando você #include <stdio.h>, isso fornece os protótipos para printf e outras funções IO.Os símbolos para essas funções são normalmente carregados pelo compilador por padrão.Você pode consultar os arquivos .h do sistema em /usr/include se estiver interessado nos idiomas normais envolvidos nesses arquivos.

Se você estiver escrevendo apenas aplicativos triviais com poucas funções, não será realmente necessário modularizar tudo em agrupamentos lógicos de procedimentos.No entanto, se você precisar desenvolver um sistema grande, precisará considerar onde definir cada uma de suas funções.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top