Typedef и Struct в файлах C и H - PullRequest
7 голосов
/ 20 марта 2010

Я использовал следующий код для создания различной структуры, но только указывал на нее людям за пределами C-файла.(Да, я знаю, что они потенциально могут с этим возиться, так что это не совсем похоже на ключевое слово private в Java, но это нормально для меня).

В любом случае, я использовал следующий код, иЯ посмотрел на него сегодня, и я очень удивлен, что он действительно работает, может кто-нибудь объяснить, почему это так?

В моем C-файле я создаю свою структуру, но не присваиваю ей тег вtypedef namespace:

struct LABall {
    int x;
    int y;
    int radius;
    Vector velocity;
};

И в файле H я помещаю это:

typedef struct LABall* LABall;

Я, очевидно, использую #include "LABall.h" в файле c, но яНЕ используйте #include "LABall.c" в заголовочном файле, так как это уничтожит всю цель отдельного заголовочного файла.Итак, почему я могу создать указатель на структуру LABall * в файле H, если я на самом деле ее не включил?Имеет ли это какое-то отношение к пространству имен struct, работающему в разных файлах, даже если один файл никоим образом не связан с другим?

Спасибо.

Ответы [ 3 ]

23 голосов
/ 20 марта 2010

Стандартный шаблон для подобных вещей - это файл 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, в этом файле неизвестен.

7 голосов
/ 20 марта 2010

Поскольку вы спрашиваете точную причину того, «почему» язык работает таким образом, я предполагаю, что вам нужны точные ссылки. Если вы найдете этот педант, просто пропустите заметки ...

Это работает из-за двух вещей:

  • Все указатели на типы структур имеют одинаковое представление (обратите внимание, что не верно для всех типов указателей, что касается стандарта C). [1] Следовательно, у компилятора достаточно информации для генерации правильного кода для всех типов использования вашего указателя на структуру.

  • Пространство имен тегов (struct, enum, union) действительно совместимо во всех единицах перевода. [2] Таким образом, две структуры (хотя одна не определена полностью, т. Е. В ней отсутствуют объявления членов) - это одно и то же.

(Кстати, #import не является стандартным.)

[1] Согласно n1256 §6.2.5.27:

Все указатели на типы конструкций должны иметь те же требования к представлению и выравниванию, что и другие. Указатели на другие типы не обязательно должны иметь одинаковые требования к представлению или выравниванию.

[2] Согласно n1256 §6.2.7.1:

два типа структуры, объединения или перечисления, объявленные в отдельных единицах перевода, совместимы, если их теги и члены удовлетворяют следующим требованиям: если один объявлен с тегом, другой должен быть объявлен с тем же тегом Если оба типа являются полными, применяются следующие дополнительные требования: [не касается нас].

1 голос
/ 20 марта 2010

В

typedef struct A* B;

, поскольку интерфейсы всех указателей одинаковы, зная, что B означает, что указатель на структуру A уже содержит достаточно информации.Реальная реализация A не имеет значения (этот метод называется «непрозрачный указатель».)

(Кстати, лучше переименовать один из LABall. Это сбивает с толку, что одно и то же имя используется для несовместимых типов.)

...