Чи мають визначення структури входити у файл .h чи .c?


102

Я бачив як повні визначення structs в заголовках, так і лише декларації - чи є якась перевага одного методу над іншим?

Якщо це має значення, я, як правило, набираю такі структури, як у .h

typedef struct s s_t;

Редагувати

Щоб було зрозуміло, параметри - це декларація у файлі заголовка та визначення у класі, або обидва оголошення та визначення у файлі заголовка. Обидва повинні приводити до однакової зручності використання, навіть якщо одна з них пов'язана, чи не так?


Я бачу багато дублікатів, наприклад тут, але точних збігів немає. Виправте мене, якщо я помиляюся в цьому плані


2
Ви хочете непрозору або непрозору структуру?

4
Бічна примітка, ідентифікатори, за допомогою _tяких зарезервовано POSIX, тому зазвичай це погана ідея. Ви могли просто зробити typedef struct toto toto.
Jens Gustedt

Я бачив багато _tінших місць (наприклад, lighttp, linux) ... і я приставлю речі до projident_ так, це не повинно бути проблемою, чи не так?
Аарон Йодайкен

І @WTP, я вважаю, що непрозорість, як правило, вважається кращою і більшою мірою C, ні (що з FILEприкладом тощо). Отже, непрозорий.
Аарон Йодайкен

Якщо це непрозора структура, вона повинна містити файл заголовка, або ваш код - НЕ ДУМКИ (не повторюйте себе).

Відповіді:


107

Приватні структури для цього файлу повинні містити файл .c з декларацією у файлі .h, якщо вони використовуються будь-якими функціями в .h.

Громадські структури повинні містити файл .h.


4
Думаю, я згоден з цією відповіддю більше. Йдеться не про використання структури через будь-які інші файли .c чи ні, це чи слід вважати структуру загальнодоступною (і так доступною) чи ні.
c00kiemon5ter

@ τεκ Ви маєте на увазі globalта localвидимість? publicне має сенсу в структурі. Усі структури за замовчуванням є загальнодоступними.
BugShotGG

3
@Geo Papas Це питання про C. publicце не ключове слово у C. Якщо ви подивитесь на відповідь Matthew Slattery нижче, ви можете побачити, як використання лише прямої заяви в заголовку викликає помилку компілятора, коли користувач намагається використовувати членів приватна (непрозора) структура.
τεκ

68

Обидва повинні приводити до однакової зручності використання, навіть якщо одна з них пов'язана, чи не так?

Ні, якщо не враховувати інші файли .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
просто хочете дізнатись, як ви створили ці кодові вікна та все-таки виділили код всередині них ... вручну? Здається, ця ОП вийшла із програми stackoverflow: '(Чи може хто-небудь ще мені сказати ....
Mahesha999

Гарний приклад! Дякую!
Віктор Хейн

Дякую за такий приклад! dereferencing pointer to incomplete typeбув саме мій випадок!
Тимур Файзрахманов

Я хотів би лише додати, що не всі загальнодоступні структури погані: ви можете, наприклад, хочете дозволити користувачеві вашого API заповнити дані та надіслати їх.
Олександр Торстлінг,

@ Mahesha999, там немає ніякої магії. ТАК виділяє код, навіть якщо ви кладете сміття в нього. Зауважте, що він намагається виділити командний рядок, виведений пізніше у публікації.
Вінгер Сендон

8

Якщо структура повинна використовуватися іншими підрозділами компіляції (.c файлами), розмістіть її у файлі заголовка, щоб ви могли включити цей заголовок файл, де це потрібно.

Якщо структура використовується лише в одній одиниці компіляції (.c-файл), ви розміщуєте її в цьому .c-файлі.


3

Справа в тому, що розміщення його у файлі заголовка дозволяє використовувати структуру (або будь-яке інше визначення) з декількох вихідних файлів, просто включивши цей файл заголовка.

Але якщо ви впевнені, що він буде використовуватися лише з одного вихідного файлу, то це насправді не має ніякого значення.



-4

Взагалі, я не думаю, що це має велике значення, вкладаєте ви їх у заголовок чи вихідні файли. Однак якщо вам потрібно отримати доступ до членів структури з декількох вихідних файлів, простіше помістити структуру у файл заголовка та включити її з будь-яких інших файлів, де потрібна структура.


8
-1: якщо ви дбаєте про хорошу інженерію програмного забезпечення (абстрагування, модульність тощо), то насправді має значення, куди ви поставите визначення структури
Пол R,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.