Typedef и структура в файлах C и H
Я использую следующий код для создания различных структур, но только даю людям за пределами файла C указатель на него. (Да, я знаю, что они потенциально могут возиться с ним, поэтому это не совсем похоже на ключевое слово private в Java, но со мной все в порядке).
в любом случае, я использовал следующий код, и я посмотрел на него сегодня, и я очень удивлен, что он действительно работает, Может кто-нибудь объяснить, почему это?
в моем файле C я создаю свой struct, но не давайте ему тег в пространстве имен typedef:
struct LABall {
int x;
int y;
int radius;
Vector velocity;
};
и в файле H я помещаю это:
typedef struct LABall* LABall;
Я, очевидно, использую #include " LABall.h "в файле c, но я не использую #include" LABall.c " в заголовочном файле, так как это уничтожит всю цель отдельного заголовочного файла. Итак, почему я могу создать указатель на LABall * struct в файле H, когда я фактически не включил его? Имеет ли это какое-то отношение к пространству имен struct работа с файлами accross, даже если один файл никоим образом не связан с другим?
спасибо.
3 ответов
поскольку вы спрашиваете точную причину "почему" язык работает таким образом, я предполагаю, что вам нужны точные ссылки. Если вы найдете этого педанта, просто пропустите заметки...
Это работает из-за двух вещей:
все указатели на типы структур имеют одинаковое представление (обратите внимание, что это не true для всех типов указателей, насколько это касается стандарта C).[1] следовательно, компилятор имеет достаточно информации для генерации правильного кода для все виды использования типа указатель-структура.
пространство имен тегов (struct, enum, union) действительно совместимо со всеми единицами перевода.[2] Таким образом, две структуры (даже если одна не полностью определена, т. е. в ней отсутствуют объявления членов) являются одним и тем же.
(кстати, #импорт нестандартен.)
[1] согласно n1256 §6.2.5.27:
все указатели на типы структур должны быть одинаковыми требования к представлению и выравниванию как друг друга. Указатели на другие типы не должны иметь одинаковые требования к представлению или выравниванию.
[2] согласно n1256 §6.2.7.1:
два типа структуры, объединения или перечисления, объявленные в отдельных единицах перевода, совместимы, если их теги и члены удовлетворяют следующим требованиям: если один объявлен с тегом, другой должен быть объявлен с тем же тегом. Если оба типа являются полными, то применяются следующие дополнительные требования: [не касается].
стандартный шаблон для таких вещей, чтобы иметь foo.h
файл, определяющий API как
typedef struct _Foo Foo;
Foo *foo_new();
void foo_do_something(Foo *foo);
и a 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
неизвестно в этом файле.
на
typedef struct A* B;
поскольку все интерфейсы указателей одинаковы, зная, что B означает указатель на структуру A, уже содержит достаточно информации. Фактическая реализация A не имеет значения (этот метод называется "непрозрачный указатель".)
(кстати, лучше переименовать LABall
' s. Это сбивает с толку, что одно и то же имя используется для несовместимых типов.)