Devo usar #include nos headers?

É necessário #include algum arquivo, se dentro de um header (* .h), os tipos definidos neste arquivo são usados?

Por exemplo, se eu usar o GLib e desejar usar o tipo básico do gchar em uma estrutura definida no meu header, é necessário fazer um #include , sabendo que eu já o tenho no meu arquivo * .c?

Se sim eu também tenho que colocá-lo entre o #ifndef e o #define ou depois do #define ?

As regras do Goddard Space Flight Center ( GSFC ) da NASA para headers em C indicam que deve ser possível include um header em um arquivo de origem como o único header, e esse código usando os resources fornecidos por esse header será então compilado.

O benefício dessa regra é que, se alguém precisar usar o header, não precisará se esforçar para descobrir quais outros headers também devem ser incluídos – eles sabem que o header fornece tudo o que é necessário.

A possível desvantagem é que alguns headers podem ser incluídos muitas vezes; é por isso que os protetores de header de múltiplas inclusões são cruciais (e porque compiladores tentam evitar a reinclusão de headers sempre que possível).

Implementação

Esta regra significa que se o header usa um tipo – como ‘ FILE * ‘ ou ‘ size_t ‘ – então deve assegurar que o outro header apropriado ( ou por exemplo) seja incluído. Um corolário, muitas vezes esquecido, é que o header não deve include nenhum outro header que não seja necessário para o usuário do pacote, a fim de usar o pacote. O header deve ser mínimo, em outras palavras.

Além disso, as regras do GSFC fornecem uma técnica simples para garantir que isso seja o que acontece:

  • No arquivo de origem que define a funcionalidade, o header deve ser o primeiro header listado.

Portanto, suponha que tenhamos um tipo de magia.

magicsort.h

 #ifndef MAGICSORT_H_INCLUDED #define MAGICSORT_H_INCLUDED #include  typedef int (*Comparator)(const void *, const void *); extern void magicsort(void *array, size_t number, size_t size, Comparator cmp); #endif /* MAGICSORT_H_INCLUDED */ 

magicsort.c

 #include  void magicsort(void *array, size_t number, size_t size, Comparator cmp) { ...body of sort... } 

Note que o header deve include algum header padrão que defina size_t ; o menor header padrão que faz isso é , embora vários outros também façam isso ( , , , possivelmente alguns outros).

Além disso, como mencionado anteriormente, se o arquivo de implementação precisar de alguns outros headers, que assim seja, é totalmente normal que alguns headers extras sejam necessários. Mas o arquivo de implementação (‘magicsort.c’) deve incluí-los e não depender de seu header para incluí-los. O header deve include apenas o que os usuários do software precisam; não o que os implementadores precisam.

Cabeçalhos de configuração

Se o seu código usa um header de configuração (GNU Autoconf e o ‘config.h’ gerado, por exemplo), você pode precisar usar isso em ‘magicsort.c’:

 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "magicsort.h" ... 

Esta é a única vez que sei que o header privado do módulo não é o primeiro header do arquivo de implementação. No entanto, a inclusão condicional de ‘config.h’ provavelmente deve estar no próprio ‘magicsort.h’.


Atualização 2011-05-01

O URL vinculado acima não é mais funcional (404). Você pode encontrar o padrão C ++ (582-2003-004) em EverySpec.com ; o padrão C (582-2000-005) parece estar faltando em ação.

As diretrizes do padrão C foram:

§2.1 UNIDADES

(1) O código deve ser estruturado como unidades ou como arquivos de header independentes.

(2) Uma unidade deve consistir em um único arquivo de header (.h) e um ou mais arquivos de corpo (.c). Coletivamente, os arquivos de header e corpo são chamados de arquivos de origem.

(3) Um arquivo de header de unidade deve conter todas as informações pertinentes requeridas por uma unidade cliente. O cliente de uma unidade precisa acessar apenas o arquivo de header para usar a unidade.

(4) O arquivo de header da unidade deve conter instruções #include para todos os outros headers exigidos pelo header da unidade. Isso permite que os clientes usem uma unidade incluindo um único arquivo de header.

(5) O arquivo do corpo da unidade deve conter uma declaração #include para o header da unidade, antes de todas as outras instruções #include. Isso permite que o compilador verifique se todas as instruções #include necessárias estão no arquivo de header.

(6) Um ficheiro do corpo deve conter apenas funções associadas a uma unidade. Um arquivo de corpo pode não fornecer implementações para funções declaradas em headers diferentes.

(7) Todas as unidades clientes que usam qualquer parte de uma determinada unidade U devem include o arquivo de header para a unidade U; isso garante que haja apenas um lugar onde as entidades na unidade U são definidas. Unidades clientes podem chamar apenas as funções definidas no header da unidade; eles não podem chamar funções definidas no corpo, mas não declaradas no header. Unidades cliente não podem acessar variables ​​declaradas no corpo, mas não no header.

Um componente contém uma ou mais unidades. Por exemplo, uma biblioteca de matemática é um componente que contém várias unidades, como vetor, matriz e quaternião.

Os arquivos de header autônomos não possuem corpos associados; por exemplo, um header de tipos comuns não declara funções, portanto não precisa de corpo.

Algumas razões para ter vários arquivos do corpo para uma unidade:

  • Parte do código do corpo depende do hardware ou do sistema operacional, mas o resto é comum.
  • Os arquivos são muito grandes.
  • A unidade é um pacote utilitário comum e alguns projetos usam apenas algumas das funções. Colocar cada function em um arquivo separado permite que o vinculador exclua os que não são usados ​​da imagem final.

§2.1.1 Cabeçalho incluem justificativa

Este padrão exige que o header de uma unidade contenha instruções #include para todos os outros headers exigidos pelo header da unidade. Colocar #include para o header da unidade primeiro no corpo da unidade permite que o compilador verifique se o header contém todas as instruções #include necessárias.

Um design alternativo, não permitido por este padrão, não permite instruções #include nos headers; todos os #include s são feitos nos arquivos do corpo. Os arquivos de header da unidade devem conter instruções #ifdef que verificam se os headers necessários estão incluídos na ordem correta.

Uma vantagem do design alternativo é que a lista #include no arquivo do corpo é exatamente a lista de dependencies necessária em um makefile, e essa lista é verificada pelo compilador. Com o design padrão, uma ferramenta deve ser usada para gerar a lista de dependencies. No entanto, todos os ambientes de desenvolvimento recomendados por agências fornecem essa ferramenta.

Uma grande desvantagem do design alternativo é que, se a lista de headers obrigatórios de uma unidade for alterada, cada arquivo que usa essa unidade deve ser editado para atualizar a lista de instruções #include . Além disso, a lista de headers necessária para uma unidade de biblioteca de compilador pode ser diferente em destinos diferentes.

Outra desvantagem do design alternativo é que os arquivos de header da biblioteca do compilador e outros arquivos de terceiros devem ser modificados para include as instruções #ifdef necessárias.

Uma prática comum diferente é include todos os arquivos de header do sistema antes de qualquer arquivo de header do projeto, em arquivos do corpo. Esse padrão não segue essa prática, porque alguns arquivos de header do projeto podem depender dos arquivos de header do sistema, porque usam as definições no header do sistema ou porque desejam replace uma definição do sistema. Esses arquivos de header do projeto devem conter instruções #include para os headers do sistema; se o corpo inclui-los primeiro, o compilador não verifica isso.

Padrão GSFC disponível via Internet Archive 2012-12-10

Informações cedidas por Eric S. Bullington :

O padrão de codificação da NASA C referenciado pode ser acessado e baixado via arquivo da Internet:

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard

Sequenciamento

A pergunta também pergunta:

Se sim, eu também tenho que colocá-lo (as linhas #include ) entre o #ifndef e o #define ou depois do #define .

A resposta mostra o mecanismo correto – os includes nesteds, etc, devem ser após o #define (e o #define deve ser a segunda linha sem comentários no header) – mas isso não explica por que isso está correto.

Considere o que acontece se você colocar o #include entre o #ifndef e o #define . Suponha que o outro header em si inclua vários headers, talvez até mesmo #include "magicsort.h" indiretamente. Se a segunda inclusão de magicsort.h ocorrer antes de #define MAGICSORT_H_INCLUDED , o header será incluído uma segunda vez antes que os tipos definidos sejam definidos. Assim, em C89 e C99, qualquer nome de tipo typedef será erroneamente redefinido (C2011 permite que eles sejam redefinidos para o mesmo tipo), e você obterá a sobrecarga de processar o arquivo várias vezes, derrotando a finalidade do protetor de header no arquivo. primeiro lugar. É também por isso que o #define é a segunda linha e não é escrito antes do #endif . A fórmula dada é confiável:

 #ifndef HEADERGUARDMACRO #define HEADERGUARDMACRO ...original content of header — other #include lines, etc... #endif /* HEADERGUARDMACRO */ 

Uma boa prática é colocar apenas #includes em um arquivo de inclusão se o arquivo de inclusão precisar deles. Se as definições em um determinado arquivo de inclusão forem usadas apenas no arquivo .c, inclua-o apenas no arquivo .c.

No seu caso, eu iria incluí-lo no arquivo de inclusão entre o # ifdef / # endif.

Isso minimizará as dependencies para que os arquivos que não precisam de um determinado include não precisem ser recompilados se o arquivo de inclusão for alterado.

Normalmente, os desenvolvedores da biblioteca protegem seus includes de múltiplos incluindo o #ifndef / #define/ #endif “truque”, para que você não tenha que fazê-lo.

É claro, você deve checar … mas, de qualquer forma, o compilador lhe dirá em algum momento 😉 De qualquer forma, é uma boa prática verificar se há inclusões múltiplas, já que isso atrasa o ciclo de compilation.

Durante o pré-processador de compilation apenas substitui a diretiva #include pelo conteúdo do arquivo especificado. Para evitar loop infinito, deve usar

 #ifndef SOMEIDENTIFIER #define SOMEIDENTIFIER ....header file body........ #endif 

Se algum header foi incluído em outro header que foi incluído em seu arquivo, não é necessário incluí-lo explicitamente novamente, porque ele será incluído no arquivo recursivamente

Sim, é necessário ou o compilador irá reclamar quando tentar compilar o código que não está “ciente”. Pense em # include’s são uma dica / cotovelada / cotovelo para o compilador para dizer a ele para pegar as declarações, estruturas etc para uma compilation bem sucedida. O truque de header # ifdef / # endif, como apontado pelo jldupont, é acelerar a compilation de código.

Ele é usado em instâncias onde você tem um compilador C ++ e compila o código C simples, como mostrado aqui. Aqui está um exemplo do truque:

 #ifndef __MY_HEADER_H__
 #define __MY_HEADER_H__

 #ifdef __cplusplus
 extern "C" {
 #fim se


 / * Código C aqui como estruturas, declarações etc. * /

 #ifdef __cplusplus
 }
 #fim se

 #endif / * __MY_HEADER_H__ * /

Agora, se isso foi incluído várias vezes, o compilador irá incluí-lo apenas uma vez, já que o símbolo __MY_HEADER_H__ é definido uma vez, o que acelera os tempos de compilation. Observe o símbolo cplusplus no exemplo acima, que é o modo padrão normal de lidar com a compilation de C ++ se você tiver um código em C por aí.

Eu incluí o acima para mostrar isso (apesar de não ser realmente relevante para a pergunta original do pôster). Espero que isso ajude, Atenciosamente, Tom.

PS: Desculpe por deixar alguém downvote isso como eu pensei que seria útil tidbit para os recém-chegados ao C / C ++. Deixe um comentário / críticas etc como eles são muito bem vindos.

Basta include todos os headers externos em um arquivo de header comum em seu projeto, por exemplo, global.he incluí-lo em todos os seus arquivos c:

Pode ser assim:

 #ifndef GLOBAL_GUARD #define GLOBAL_GUARD #include  /*...*/ typedef int YOUR_INT_TYPE; typedef char YOUR_CHAR_TYPE; /*...*/ #endif 

Este arquivo usa include guard para evitar inclusões múltiplas, múltiplas definições ilegais, etc.

Você precisa include o header no header e não há necessidade de incluí-lo no .c. Includes deve ir após o #define para que eles não sejam desnecessariamente incluídos várias vezes. Por exemplo:

 /* myHeader.h */ #ifndef MY_HEADER_H #define MY_HEADER_H #include  struct S { gchar c; }; #endif /* MY_HEADER_H */ 

e

 /* myCode.c */ #include "myHeader.h" void myFunction() { struct S s; /* really exciting code goes here */ } 

Eu uso a seguinte construção para ter certeza de que o arquivo de inclusão necessário está incluído antes dessa inclusão. Eu incluo todos os arquivos de header apenas nos arquivos de origem.

 #ifndef INCLUDE_FILE_H #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h" #endif 

O que eu normalmente faço é criar um único arquivo de inclusão que inclua todas as dependencies necessárias na ordem correta. Então eu posso ter:

 #ifndef _PROJECT_H_ #define _PROJECT_H_ #include  #include "my_project_types.h" #include "my_project_implementation_prototypes.h" #endif 

Tudo em project.h. Agora o project.h pode ser incluído em qualquer lugar sem requisitos de pedido, mas eu ainda tenho o luxo de ter minhas dependencies, tipos e protótipos de function de API em headers diferentes.