И то, и другое должно приводить к одинаковому удобству использования, даже если они связаны ссылками, не так ли?
Нет, если вы рассматриваете другие файлы .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
$
Таким образом, версия «определение в реализации» защищает от случайного или преднамеренного неправильного использования подробностей частной реализации.