Oba powinny skutkować taką samą użytecznością, nawet jeśli jest to połączenie, prawda?
Nie, nie biorąc pod uwagę innych plików .c zawierających ten sam nagłówek. Jeśli definicja struktury nie jest widoczna dla kompilatora, nie można użyć szczegółów tej definicji. Deklaracja bez definicji (np. Po prostu struct s;
) powoduje niepowodzenie kompilatora, jeśli cokolwiek próbuje zajrzeć do środka struct s
, jednocześnie pozwalając mu np. Na kompilację struct s *foo;
(o ile foo
nie zostanie później dereferencjonowana).
Porównaj te wersje api.h
i 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; |
| } |
+---------------------------------+
Ten klient interfejsu API działa z dowolną wersją:
#include "api.h"
void good(struct s *foo)
{
api_func(foo, 123);
}
Ten przegląda szczegóły implementacji:
#include "api.h"
void bad(struct s *foo)
{
foo->internal = 123;
}
który będzie działał z wersją "definicja w nagłówku", ale nie z wersją "definicja w implementacji", ponieważ w tym drugim przypadku kompilator nie ma widoczności układu struktury:
$ gcc -Wall -c bad.c
bad.c: In function 'bad':
bad.c:5: error: dereferencing pointer to incomplete type
$
Tak więc wersja „definicja we wdrożeniu” chroni przed przypadkowym lub umyślnym niewłaściwym wykorzystaniem prywatnych szczegółów implementacji.