Como declarar uma estrutura em um cabeçalho que deve ser usado por vários arquivos em c?


115

Se eu tiver um arquivo source.c com uma estrutura:

struct a { 
    int i;
    struct b {
        int j;
    }
};

Como essa estrutura pode ser usada em outro arquivo (ou seja func.c)?

Devo criar um novo arquivo de cabeçalho, declarar a estrutura lá e incluir esse cabeçalho func.c?

Ou devo definir a estrutura inteira em um arquivo de cabeçalho e incluí-la em source.ce func.c? Como a estrutura pode ser declarada externem ambos os arquivos?

Eu deveria typedef? Se sim, como?


Observe que a definição da estrutura não é válida C. No mínimo deve haver um ponto e vírgula após a chave de fechamento para struct b, mas então sua estrutura adeclara um tipo que não é usado (você provavelmente deve definir um nome de membro, talvez k, após a chave de fechamento interna e antes do ponto e vírgula.
— Jonathan Leffler

Respostas:


140

se esta estrutura deve ser usada por algum outro arquivo func.c como fazer isso?

Quando um tipo é usado em um arquivo (ou seja, arquivo func.c), ele deve estar visível. A pior maneira de fazer isso é copiar e colar em cada arquivo de origem necessário.

A maneira certa é colocá-lo em um arquivo de cabeçalho e incluí-lo sempre que necessário.

devemos abrir um novo arquivo de cabeçalho e declarar a estrutura lá e incluir esse cabeçalho no func.c?

Esta é a solução que eu mais gosto, porque torna o código altamente modular. Eu codificaria sua estrutura como:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

struct a
{ 
    int i;
    struct b
    {
        int j;
    }
};

#endif

Eu colocaria funções que usam essa estrutura no mesmo cabeçalho (a função que é "semanticamente" parte de sua "interface").

E normalmente, eu poderia nomear o arquivo com o nome da estrutura e usar esse nome novamente para escolher as definições dos protetores de cabeçalho.

Se você precisar declarar uma função usando um ponteiro para a estrutura, não precisará da definição completa da estrutura. Uma declaração de encaminhamento simples como:

struct a ;

Será suficiente, e diminui o acoplamento.

ou podemos definir a estrutura total no arquivo de cabeçalho e incluí-la em source.c e func.c?

Esta é outra maneira, um pouco mais fácil, mas menos modular: alguns códigos que precisam apenas de sua estrutura para funcionar ainda precisam incluir todos os tipos.

Em C ++, isso pode levar a complicações interessantes, mas isso está fora do tópico (sem tag C ++), então não vou entrar em detalhes.

a seguir, como declarar essa estrutura como externa em ambos os arquivos. ?

Não consigo entender o ponto, talvez, mas Greg Hewgill tem uma resposta muito boa em seu post Como declarar uma estrutura em um cabeçalho que deve ser usado por vários arquivos em c? .

devemos digitá-lo então como?

  • Se você estiver usando C ++, não.
  • Se você estiver usando C, você deve.

O motivo é que o gerenciamento de C struct pode ser uma dor: você deve declarar a palavra-chave struct em todos os lugares em que ela é usada:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Enquanto um typedef permitirá que você escreva sem a palavra-chave struct.

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

É importante que você ainda mantenha um nome para a estrutura. Escrevendo:

typedef struct
{
   /* etc. */
} MyStruct ;

irá apenas criar uma estrutura anônima com um nome digitado, e você não poderá encaminhá-la. Portanto, mantenha o seguinte formato:

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

Assim, você será capaz de usar MyStruct em todos os lugares que deseja evitar adicionar a palavra-chave struct e ainda usar MyStructTag quando um typedef não funcionar (ou seja, declaração de encaminhamento)

Editar:

Corrigida a suposição errada sobre a declaração de struct C99, conforme devidamente observado por Jonathan Leffler .

Editar 01/06/2018:

Craig Barnes nos lembra em seu comentário que você não precisa manter nomes separados para o nome de struct "tag" e seu nome "typedef", como fiz acima por uma questão de clareza.

Na verdade, o código acima pode muito bem ser escrito como:

typedef struct MyStruct
{
   /* etc. */
} MyStruct ;

IIRC, isso é realmente o que C ++ faz com sua declaração de struct mais simples, nos bastidores, para mantê-la compatível com C:

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

De volta ao C, eu vi os dois usos (nomes separados e mesmos nomes) e nenhum tem desvantagens que eu conheça, então usar o mesmo nome torna a leitura mais simples se você não usar "namespaces" separados em C para structs e outros símbolos .


2
Você pode comentar ou apontar a seção do padrão C99 que garante o seu comentário "Se você estiver usando o C99, não"?
— Jonathan Leffler

Você está certo. Testei recentemente um C99 e fiquei surpreso ao ver que minha estrutura sem tipo não foi reconhecida quando usada da maneira C ++. Eu procurei por opções de compilador, e então, em todos os documentos padrão que pude colocar minhas mãos, mas não encontrei nada que pudesse explicar Eu acreditava que era possível ...
— paercebal

2
... De qualquer forma, obrigado pelo comentário. Eu corrigi isso agora.
— paercebal

Não há necessidade de usar nomes diferentes para a structtag e o typedefnome. C usa um namespace diferente para structtags, então você pode usar MyStructpara ambos.
— Craig Barnes

1
@CraigBarnes Você está certo, mas eu queria que isso ficasse claro apenas lendo o código. Se eu tivesse dado o mesmo nome, poderia ter confundido C novatos sobre a necessidade de escrever o nome * duas vezes "na mesma" declaração ". Vou adicionar uma nota mencionando seu comentário. Obrigado!
— paercebal

34

Para uma definição de estrutura que deve ser usada em mais de um arquivo de origem, você definitivamente deve colocá-la em um arquivo de cabeçalho. Em seguida, inclua esse arquivo de cabeçalho em qualquer arquivo de origem que precise da estrutura.

A externdeclaração não é usada para definições de estrutura, mas em vez disso, é usada para declarações de variáveis ​​(ou seja, algum valor de dados com um tipo de estrutura que você definiu). Se você quiser usar a mesma variável em mais de um arquivo de origem, declare-a como externem um arquivo de cabeçalho como:

extern struct a myAValue;

Então, em um arquivo de origem, defina a variável real:

struct a myAValue;

Se você esquecer de fazer isso ou acidentalmente defini-lo em dois arquivos de origem, o vinculador o informará sobre isso.


Você pode obter um erro de vinculador ou não. Em C, o modelo de ligação permite definições 'comuns', portanto, várias definições sem inicializador (e talvez com o mesmo inicializador) podem funcionar. É uma 'extensão comum'. Alguns compiladores também suportam definições provisórias (ausentes), acredito.
— Jonathan Leffler

Então, em um arquivo de origem, defina a variável real:; ... ou acidentalmente defini- lo em dois arquivos de origem ... :)
— Johannes Schaub - litb

Uma técnica que usei para o problema de declaração / definição é definir condicionalmente GLOBALcomo externou nada no topo do cabeçalho e, em seguida, declarar as variáveis ​​como GLOBAL struct a myAValue;. Da maioria dos arquivos de origem, você organiza a #define GLOBAL externversão a ser usada ( declarando as variáveis) e de exatamente um arquivo de origem faz com que o define vazio seja usado para que as variáveis ​​sejam definidas .
— TripeHound

Você pode ter o nome da estrutura igual ao nome do typedef em C, mas não em C ++.
— xuhdev

6

ah:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

pronto, agora você só precisa incluir ah nos arquivos onde deseja usar esta estrutura.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.