Определение типа и Структура в C и файлах H

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

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

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

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

И в файле H, я поместил это:

typedef struct LABall* LABall;

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

Спасибо.

7
задан Leif Andersen 20 March 2010 в 16:54
поделиться

3 ответа

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

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

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

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

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

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

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

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

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

7
ответ дан 6 December 2019 в 05:55
поделиться

В

typedef struct A* B;

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

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

1
ответ дан 6 December 2019 в 05:55
поделиться

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

23
ответ дан 6 December 2019 в 05:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: