Использование Typedef Struct в программировании на C - PullRequest
0 голосов
/ 01 декабря 2018

У меня есть шаблон C, который мне дают в качестве домашней работы.Но прежде чем делать домашнее задание, мне нужно понять использование "typedef" и "struct" , чтобы четко перейти к кодированию.Вот код:

typedef struct NODE_s *NODE;
typedef struct NODE_s
{
    NODE right;
    NODE left;
    unsigned long data;
    int height;
} NODE_t[1];

typedef struct TREE_s *TREE;
typedef struct TREE_s
{
    NODE root;
} TREE_t[1];

TREE tree_init();
NODE node_init(unsigned long data);

Прежде всего, что здесь делает строка typedef struct NODE_s *NODE;?Почему он назван как указатель с *?

Затем, после определения структуры, какова цель создания переменной с именем NODE_t[1], это квадратные скобки с номером "1" что-то связанное с массивом, или этоэто просто что-то еще?

Наконец, когда объявляются функции tree_init(); и node_init(unsigned long data);, почему имена типов данных TREE и NODE используются наоборотони были объявлены как *TREE и *NODE до определения структуры?Спасибо за ответы.

1 Ответ

0 голосов
/ 01 декабря 2018

Шаблон, который вам дали, это, где я добавил цифры к некоторым строкам для удобства:

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);

В чем здесь проблемы?

  1. Как отмечается в комментариях, SO Q & A Является ли хорошей идеей вводить указатели на определение типа предполагает, что не стоит вводить указатели на определение типа, с ограниченными исключениями для «указателей на функции» (здесь не имеет значения) ивозможно (но, вероятно, нет) для непрозрачных типов.Эта строка делает две вещи: (1) она говорит: «существует тип структуры с тегом NODE_s; (2) имя NODE является синонимом для struct NODE_s *. Тип структуры на данный момент не завершен; нетподробности о его членах известны.
  2. Эта строка начинает новый typedef, но также запускает определение типа struct NODE_s (из-за {, который следует в следующей строке).
  3. Тип NODE уже известен, его можно использовать здесь. Это означает, что вы написали struct NODE_s *right;.
  4. Имя NODE_t является псевдонимом для типа * 1024.*, массив 1 struct NODE_s. Не ясно, для чего это будет использоваться. У меня есть оговорки (в лучшем случае) относительно его существования. (Вы также можете применить обсуждение в пунктах 1, 2, 4 к типу struct TREE_s, mutatis mutandis.)
  5. Это объявление функции, но не объявление прототипа. Оно говорит, что функцию 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 (потому что это массив).

...