Проблема со структурами, где их размещать и как ссылаться на них в заголовках?

Хорошо, сейчас у меня дилемма, и ни мое знание C (которое в любом случае не является самым большим), ни всемогущий Google, похоже, не могут мне в этом помочь:

У меня есть несколько структур для прототипа игры:

  • Map (Эээ ... Карта ..)
  • Chara (База для врагов игроков)
  • Player (Игрок)

Теперь проблема в следующем: Map нужна ссылка на Player, который на нем, Player строится, давая ему Map и Chara, а Chara тоже нужен Map.

Если я объявляю структуры в файлах заголовков и оборачиваю их #ifndef, я попадаю в цикл циклических зависимостей, когда включаю заголовки из других заголовков.

Когда я объявляю структуры в файлах .c, я использую extern struct Map map и т. Д., Но затем сталкиваюсь с проблемой типа invalid use of incomplete declaration или forward declaration of struct XXX.

Здесь 4 часа утра, и я хотел бы больше потратить свое время на переписывание материала движка (который уже существует как в Python, так и в JavaScript ... да, у меня слишком много времени!), А не пробовать все возможное. комбинация поисковых запросов на остаток ночи.

Я понимаю, что это может быть ДЕЙСТВИТЕЛЬНО простая вещь, но здесь 30 ° C, так что, пожалуйста, проявите милосердие к моим "навыкам" C ^^

ИЗМЕНИТЬ
Поскольку моя проблема использовала typedefs, а ответ caf не включал их, мне пришлось немного повозиться с ним, чтобы все заработало. Поэтому, чтобы помочь людям, которые могут найти этот ответ через SE, я добавлю следующий код:

map.h

typedef struct _Player Player;

typedef struct _Map {
    ...
    Player *player;
    ...
} Map;

map.c

// include both player.h and chara.h

player.h

typedef struct _Map Map;
typedef struct _Chara Chara;

typedef struct _Player {
    Chara *chara;
    ...
} Player;

Player *player_create(Map *map, int x, int y);

player.c

// include player.h, chara.h and map.h

person Ivo Wetzel    schedule 01.07.2010    source источник
comment
Просто используйте Персонаж. Чара, наверное, самая раздражающая вещь, которую я видел за весь день.   -  person Catharsis    schedule 01.07.2010
comment
Побочная ошибка: не используйте имена, начинающиеся с _, для ваших структур. В зависимости от регистра следующего символа и наличия только одного или двух подчеркиваний, они могут быть зарезервированы для реализации C / OS. Вместо того, чтобы пытаться запомнить, какие из них вам разрешено использовать, а какие нет, лучше просто полностью избегать имен, начинающихся с _. То же самое и с защитой от нескольких включений. Используйте #ifndef PLAYER_H, а не #ifndef _PLAYER_H. Причина, по которой стандартные заголовки C используют _, заключается в том, что они являются частью компилятора / ОС и пытаются избежать конфликтов с именами, зарезервированными для вас.   -  person R.. GitHub STOP HELPING ICE    schedule 02.07.2010


Ответы (2)


Это работает, если ваши структуры содержат только указатели на другие структуры:

map.h

#ifndef _MAP_H
#define _MAP_H

struct player;

struct map {
    struct player *player;
    ...
};

#endif /* _MAP_H */

chara.h

#ifndef _CHARA_H
#define _CHARA_H

struct map;

struct chara {
    struct map *map;
    ...
};

#endif /* _CHARA_H */

player.h

#ifndef _PLAYER_H
#define _PLAYER_H

struct map;
struct chara;

struct player {
    struct map *map;
    struct chara *chara;
    ...
};

#endif /* _PLAYER_H */

Если одна из ваших структур содержит фактические экземпляры других структур (включая массивы), тогда ей потребуется #include другой заголовок. Например, если map содержит массив игроков:

map.h

#ifndef _MAP_H
#define _MAP_H

#include "player.h"

struct map {
    struct player p[10];
    ...
};

#endif /* _MAP_H */

Тем не менее, вы должны быть осторожны с циркулярным включением. Если вы включили map.h в player.h, вы не сможете включить player.h в другой исходный файл до map.h - поэтому вы не сделаете этого.

person caf    schedule 01.07.2010
comment
Спасибо за примеры! Первый, наконец, привел меня на правильный путь, теперь все работает отлично и здорово :) - person Ivo Wetzel; 01.07.2010
comment
Согласно вопросу, карте нужен указатель / ссылка на игрока. В этом случае второй метод усложняется и, как мне кажется, требует прямой ссылки. - person sje397; 01.07.2010
comment
Фактически ваш второй метод не работает. Когда игрок включает карту, определяется _PAYER_H - это означает, что когда карта включает игрока, ничего не происходит. Ошибка будет сгенерирована для любой ссылки или указателя на игрока на карте. - person sje397; 01.07.2010
comment
@ sje397: Да, я тоже только что пришел к такому выводу, отсюда и обновление. - person caf; 01.07.2010
comment
Вы также можете поместить typedef struct foo foo; в начало файла .h. Таким образом, вам даже не нужно везде повторять ключевое слово struct. - person Patrick Schlüter; 01.07.2010

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

person Anon.    schedule 01.07.2010