Должны ли определения структур помещаться в файл .h или .c?

104

Я видел как полные определения structs в заголовках, так и просто объявления - есть ли преимущества у одного метода перед другим?

Если это имеет значение, я обычно набираю такую ​​структуру в .h

typedef struct s s_t;

редактировать

Для ясности, параметры - это объявление в файле заголовка и определение в классе или и объявление, и определение в файле заголовка. Оба должны приводить к одинаковому удобству использования, даже если одно связано с ним, не так ли?


Я вижу много почти дубликатов, например здесь, но нет точных совпадений. Пожалуйста, поправьте меня, если я ошибаюсь в этом плане.

Аарон Йодайкен
источник
2
Вам нужна непрозрачная или непрозрачная структура?
4
Примечание: идентификаторы с _tзарезервированы POSIX, так что это обычно плохая идея. Вы могли бы просто сделать typedef struct toto toto.
Йенс Густедт
Я видел много _tдругих мест (например, lighttp, linux) ... и я ставлю перед вещами префикс projident_, так что это не должно быть проблемой, не так ли?
Аарон Йодайкен
И @WTP, я думаю, что непрозрачный, как правило, считается лучше, а что Cнет (как в FILEпримере и т. Д.). Итак, непрозрачный.
Аарон Йодайкен
Если это непрозрачная структура, она должна быть помещена в файл заголовка, или ваш код не СУХИЙ (не повторяйтесь).

Ответы:

109

Частные структуры для этого файла должны находиться в файле .c с объявлением в файле .h, если они используются какими-либо функциями в .h.

Публичные структуры должны быть в файле .h.

τεκ
источник
4
Думаю, я больше согласен с этим ответом. Речь идет не об использовании структуры через какие-либо другие файлы .c или нет, а о том, должна ли структура считаться общедоступной (и, следовательно, доступной) или нет.
c00kiemon5ter
@ τεκ Ты имеешь ввиду globalи localнаглядность? publicне имеет смысла в структуре. По умолчанию все структуры общедоступны.
BugShotGG 06
3
@Geo Papas Это вопрос о C. publicне является ключевым словом в C. Если вы посмотрите на ответ Мэтью Слэттери ниже, вы увидите, как использование только прямого объявления в заголовке вызывает ошибку компилятора, когда пользователь пытается использовать члены частная (непрозрачная) структура.
τεκ 06
69

Оба должны приводить к одинаковому удобству использования, даже если одно связано с ним, не так ли?

Нет, если рассматривать другие файлы .c с таким же заголовком. Если определение структуры не видно компилятору, детали этого определения использовать нельзя. Объявление без определения (например, просто struct s;) приводит к сбою компилятора, если что-то пытается заглянуть внутрь struct s, при этом позволяя ему, например, компилировать struct s *foo;(до тех пор, fooпока не будет разыменован позже).

Сравните эти версии api.hи 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;          |
                                      | }                               |
                                      +---------------------------------+

Этот клиент API работает с любой версией:

#include "api.h"

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

Вот он в деталях реализации:

#include "api.h"

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

который будет работать с версией «определение в заголовке», но не с версией «определение в реализации», поскольку в последнем случае компилятор не имеет видимости макета структуры:

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

Таким образом, версия «определение в реализации» защищает от случайного или преднамеренного неправильного использования частных деталей реализации.

Мэтью Слэттери
источник
3
просто хотите знать, как вы создали эти окна кода и по-прежнему выделяли код внутри них ... вручную? Этот OP, похоже, оставил использование stackoverflow: '(Кто-нибудь еще может мне сказать ....
Mahesha999,
Хороший пример! Спасибо!
Виктор Хейн
Спасибо за такой пример! dereferencing pointer to incomplete typeбыл именно мой случай!
Тимур Файзрахманов
Я просто хотел бы добавить, что не все общедоступные структуры плохи: например, вы можете разрешить пользователю вашего API заполнять данные и отправлять их.
Александр Торстлинг,
@ Mahesha999, никакой магии там нет. SO выделяет код, даже если вы кладете в него мусор. Обратите внимание, что он пытается выделить вывод командной строки позже в сообщении.
Winger Sendon
8

Если структура должна использоваться другими единицами компиляции (файлы .c), поместите ее в файл заголовка, чтобы вы могли включить этот файл заголовка везде, где это необходимо.

Если структура используется только в одной единице компиляции (файл .c), вы помещаете ее в этот файл .c.

нет
источник
3

Дело в том, что размещение его в файле заголовка позволяет вам использовать структуру (или любое другое определение) из нескольких исходных файлов, просто включив этот файл заголовка.

Но если вы уверены, что он будет использоваться только из одного исходного файла, тогда это не имеет никакого значения.

Джонатан Вуд
источник
-4

В общем, я не думаю, что это имеет большое значение, помещаете ли вы их в заголовочные или исходные файлы. Однако, если вам нужно получить доступ к элементам структуры из нескольких исходных файлов, проще поместить структуру в файл заголовка и включить ее из любых других файлов, где структура необходима.

Frxstrem
источник
8
-1: если вы заботитесь о хорошей разработке программного обеспечения (абстракция, модульность и т. Д.), Тогда действительно имеет значение, где вы поместите определение структуры
Пол Р