Шаблон, который вам дали, это, где я добавил цифры к некоторым строкам для удобства:
typedef struct NODE_s *NODE; // 1
typedef struct NODE_s // 2
{
NODE right; // 3
NODE left;
unsigned long data;
int height;
} NODE_t[1]; // 4
typedef struct TREE_s *TREE;
typedef struct TREE_s
{
NODE root;
} TREE_t[1];
TREE tree_init(); // 5
NODE node_init(unsigned long data);
В чем здесь проблемы?
- Как отмечается в комментариях, SO Q & A Является ли хорошей идеей вводить указатели на определение типа предполагает, что не стоит вводить указатели на определение типа, с ограниченными исключениями для «указателей на функции» (здесь не имеет значения) ивозможно (но, вероятно, нет) для непрозрачных типов.Эта строка делает две вещи: (1) она говорит: «существует тип структуры с тегом
NODE_s
; (2) имя NODE
является синонимом для struct NODE_s *
. Тип структуры на данный момент не завершен; нетподробности о его членах известны. - Эта строка начинает новый
typedef
, но также запускает определение типа struct NODE_s
(из-за {
, который следует в следующей строке). - Тип
NODE
уже известен, его можно использовать здесь. Это означает, что вы написали struct NODE_s *right;
. - Имя
NODE_t
является псевдонимом для типа * 1024.*, массив 1 struct NODE_s
. Не ясно, для чего это будет использоваться. У меня есть оговорки (в лучшем случае) относительно его существования. (Вы также можете применить обсуждение в пунктах 1, 2, 4 к типу struct TREE_s
, mutatis mutandis.) - Это объявление функции, но не объявление прототипа. Оно говорит, что функцию
tree_init()
можно вызывать с любым числом.аргументов любого типа, потому что не указана информация о количестве или типеэти аргументы.Мы знаем, что это не переменная функция (список переменных переменных, например printf()
), потому что эти должны имеют полное объявление прототипа, заканчивающееся областью , ...)
, прежде чем они будут использованы.Если вы хотите указать, что функция не принимает аргументов, скажем так: TREE tree_init(void);
.
Я думаю, что намерение за типами NODE_t
и TREE_t
состоит в том, чтобы позволить вам писатьпример:
int main(void)
{
TREE_t x = { 0 };
if (x->root != 0)
return 1;
return 0;
}
Я не уверен, достаточно ли это полезно для подтверждения типа по сравнению с использованием struct NODE_s x
и использованием x.root
в тесте.Это избавляет вас от необходимости добавлять &
при передаче указателя на функцию;Опять же, я не уверен, что это действительно достаточно полезно, чтобы оправдать его существование.
То, что я предпочел бы видеть в качестве шаблона:
typedef struct NODE_s NODE;
struct NODE_s
{
NODE *right;
NODE *left;
unsigned long data;
int height;
};
typedef struct TREE_s TREE;
struct TREE_s
{
NODE *root;
};
extern TREE *tree_init(void);
extern NODE *node_init(unsigned long data);
Это удаляет указатели из typedef
операторов, избегает несколько своеобразных типов массивов и использует явный прототип для tree_init()
.Лично я предпочитаю, чтобы объявления функций отмечались extern
;в заголовке они будут соответствовать extern
для тех редких глобальных переменных, которые объявлены в заголовке.Многие люди предпочитают не использовать extern
, потому что компилятор предполагает, что так или иначе - пусть будет так;самая важная вещь - последовательность.
Код в main()
теперь будет записан:
int main(void)
{
TREE x = { 0 };
if (x.root != 0)
return 1;
return 0;
}
Разница?Стрелка ->
изменилась на точку .
.Там не много проблем.Однако при вызове функций вы, вероятно, будете использовать &x
, тогда как с типом TREE_t
вы просто напишите x
(потому что это массив).