Стандартный шаблон для подобных вещей - это файл foo.h
, определяющий API, например,
typedef struct _Foo Foo;
Foo *foo_new();
void foo_do_something(Foo *foo);
и foo.c
файл, обеспечивающий реализацию для этого API, например
struct _Foo {
int bar;
};
Foo *foo_new() {
Foo *foo = malloc(sizeof(Foo));
foo->bar = 0;
return foo;
}
void foo_do_something(Foo *foo) {
foo->bar++;
}
Это скрывает всю структуру памяти и размер структуры в реализации в foo.c
, и интерфейс, предоставляемый через foo.h
, полностью независим от этих внутренних объектов: A caller.c
, который имеет только #include "foo.h"
, будет иметь только хранить указатель на что-то, а указатели всегда одного размера:
#include "foo.h"
void bleh() {
Foo *f = foo_new();
foo_do_something(f);
}
Примечание: Я оставил освобождение памяти читателю в качестве упражнения. : -)
Конечно, это означает, что следующий файл broken.c
будет НЕ работать:
#include "foo.h"
void broken() {
Foo f;
foo_do_something(&f);
}
, поскольку объем памяти, необходимый для фактического создания переменной типа Foo
, в этом файле неизвестен.