As definições de struct devem estar no arquivo .h ou .c?

102

Eu vi as definições completas de structs em cabeçalhos e apenas declarações - há alguma vantagem em um método sobre o outro?

Se isso faz diferença, eu geralmente digitei uma estrutura como esta no .h

typedef struct s s_t;

Editar

Para ficar claro, as opções são declaração no arquivo de cabeçalho e definição na classe, ou ambas, declaração e definição no arquivo de cabeçalho. Ambos deveriam resultar na mesma usabilidade, mesmo que seja por ligação, não deveriam?


Vejo muitas quase duplicatas, por exemplo, aqui, mas nenhuma correspondência exata. Por favor, corrija-me se eu estiver errado a esse respeito.

Aaron Yodaiken
fonte
2
Você quer uma estrutura opaca ou não opaca?
4
Nota lateral, os identificadores com _tsão reservados pelo POSIX, então isso geralmente é uma má ideia. Você poderia apenas fazer typedef struct toto toto.
Jens Gustedt
Já vi muito _tuso em outros lugares (por exemplo, lighttp, linux) ... e prefixo as coisas com projident_ então, isso não deveria ser um problema, deveria?
Aaron Yodaiken
E @WTP, acho que não opaco é geralmente considerado melhor e mais Cish, não (com o FILEexemplo etc). Portanto, não opaco.
Aaron Yodaiken
Se for uma estrutura não opaca, ela terá que ir em um arquivo de cabeçalho, ou seu código não é DRY (não se repita).

Respostas:

107

Estruturas privadas para esse arquivo devem ir no arquivo .c, com uma declaração no arquivo .h, se forem usadas por quaisquer funções no arquivo .h.

As estruturas públicas devem ir no arquivo .h.

τεκ
fonte
4
Acho que concordo mais com esta resposta. Não se trata de usar a estrutura por meio de qualquer outro arquivo .c ou não, é se a estrutura deve ser considerada pública (e, portanto, acessível) ou não.
c00kiemon5ter
@ τεκ Você quer dizer globale localvisibilidade? publicnão faz sentido em uma estrutura. Todas as estruturas são públicas por padrão.
BugShotGG
3
@Geo Papas Esta é uma pergunta sobre C. publicnão é uma palavra-chave em C. Se você olhar a resposta de Matthew Slattery abaixo, verá como usar apenas uma declaração de encaminhamento no cabeçalho causa um erro do compilador quando o usuário tenta usar membros de um estrutura privada (opaca).
τεκ
68

Ambos deveriam resultar na mesma usabilidade, mesmo que seja por ligação, não deveriam?

Não, não quando você considera outros arquivos .c incluindo o mesmo cabeçalho. Se a definição da estrutura não estiver visível para o compilador, os detalhes dessa definição não podem ser usados. Uma declaração sem uma definição (por exemplo, apenas struct s;) faz com que o compilador falhe se algo tentar olhar para dentro struct s, enquanto ainda permite, por exemplo, compilar struct s *foo;(desde que foonão seja desreferenciado posteriormente).

Compare essas versões de api.he api.c:

Definition in header:                 Definition in implementation:
+---------------------------------+   +---------------------------------+
| struct s {                      |   | struct s;                       |
|     int internal;               |   |                                 |
|     int other_stuff;            |   | extern void                     |
| };                              |   | api_func(struct s *foo, int x); |
|                                 |   +---------------------------------+
| extern void                     |   +---------------------------------+
| api_func(struct s *foo, int x); |   | #include "api.h"                |
+---------------------------------+   |                                 |
+---------------------------------+   | struct s {                      |
| #include "api.h"                |   |     int internal;               |
|                                 |   |     int other_stuff;            |
| void                            |   | };                              |
| api_func(struct s *foo, int x)  |   |                                 |
| {                               |   | void                            |
|     foo->internal = x;          |   | api_func(struct s *foo, int x)  |
| }                               |   | {                               |
+---------------------------------+   |     foo->internal = x;          |
                                      | }                               |
                                      +---------------------------------+

Este cliente da API funciona com uma das versões:

#include "api.h"

void good(struct s *foo)
{
    api_func(foo, 123);
}

Este examina os detalhes de implementação:

#include "api.h"

void bad(struct s *foo)
{
    foo->internal = 123;
}

que funcionará com a versão "definição no cabeçalho", mas não com a versão "definição na implementação", pois neste último caso o compilador não tem visibilidade do layout da estrutura:

$ gcc -Wall -c bad.c
bad.c: In function 'bad':
bad.c:5: error: dereferencing pointer to incomplete type
$

Portanto, a versão "definição na implementação" protege contra o uso indevido acidental ou deliberado de detalhes de implementação privados.

Matthew Slattery
fonte
3
só quero saber como você criou essas janelas de código e ainda tem o código realçado dentro delas ... manualmente? Este OP parece ter saído usando stackoverflow: '(Alguém pode me dizer ....
Mahesha999
Bom exemplo! Obrigado!
Victor Haine,
Obrigado por esse exemplo! dereferencing pointer to incomplete typefoi exatamente o meu caso!
Timur Fayzrakhmanov
Gostaria apenas de acrescentar que nem todos os structs acessíveis ao público são ruins: você pode, por exemplo, permitir que o usuário de sua API preencha os dados e os envie.
Alexander Torstling
@ Mahesha999, não há mágica aí. SO realça o código mesmo se você colocar lixo nele. Observe que ele tenta destacar a saída da linha de comando posteriormente na postagem.
Winger Sendon
8

Se a estrutura for usada por outras unidades de compilação (arquivos .c), coloque-a no arquivo de cabeçalho para que possa incluí-lo sempre que necessário.

Se a estrutura for usada apenas em uma unidade de compilação (arquivo .c), coloque-a nesse arquivo .c.

nos
fonte
3

O ponto é, colocá-lo em um arquivo de cabeçalho permite que você use a estrutura (ou qualquer outra definição) de vários arquivos de origem, apenas incluindo esse arquivo de cabeçalho.

Mas se você tiver certeza de que ele só será usado a partir de um arquivo de origem, isso realmente não fará nenhuma diferença.

Jonathan Wood
fonte
-4

Geralmente, não acho que faça uma grande diferença colocá-los no cabeçalho ou nos arquivos de origem. No entanto, se você precisar acessar os membros de uma estrutura de vários arquivos de origem, é mais fácil colocar a estrutura em um arquivo de cabeçalho e incluí-lo de quaisquer outros arquivos em que a estrutura seja necessária.

Frxstrem
fonte
8
-1: se você se preocupa com uma boa engenharia de software (abstração, modularidade, etc), então realmente importa onde você coloca a definição da estrutura
Paul R