Указатели на другие структуры внутри структуры

#c #pointers #data-structures #struct

#c #указатели #структуры данных #структура

Вопрос:

Мой лектор представил следующий фрагмент кода с недостаточным объяснением того, что он представляет.

 typedef struct _s {
    int         value;
    struct _s   *next;
}  STACKITEM;

STACKITEM    *stack = NULL;
  
  1. Я понимаю, что такое указатели и структуры. Однако я не знаю, что значит иметь указатель на структуру внутри структуры. Пожалуйста, проясните и доработайте эту концепцию.

  2. Я не понимаю, почему структура объявлена typedef . Это кажется избыточным по следующим причинам:

Типичная структура гласит следующее.

 struct struct_format_name { 
members;
} individual_struct_object_name;
  

Поэтому мы объявили объект структурой и присвоили этому формату структуры имя _s. Итак, в чем был смысл использования typedef? За исключением ключевого слова typedef, это тот же формат, который вы использовали бы для объявления любой структуры.

  1. Я хотел бы уточнить разницу между указателем на структуру, указывающую на структурированный формат, как сделано выше, и указателем на структуру, указывающую на конкретную структуру.

Я подозреваю, что указатель на структуру, указывающий на структурированный формат, как сделано выше, может указывать на ЛЮБУЮ структуру этого формата? Однако указатель на структуру, указывающий на конкретную структуру, может указывать только на эту конкретную структуру, а не на другие структуры того же формата?

Комментарии:

1. Один вопрос на вопрос, пожалуйста.

2. Да, typedef бесполезен (в большинстве случаев) и сбивает с толку (как в вашем случае) Мой совет: если вам это не нужно: не используйте его.

3. Первая структура представляет собой связанный список: цепочку объектов, где каждый объект указывает на следующий в строке. Typedef может быть удобством или, что более вероятно: скрыть реализацию. Очень похоже FILE на typedef базовой файловой структуры.

4. Ответ Xaxxion не противоречит моему мнению. Фактически, он утверждает, что в конце две формы эквивалентны (результат тот же), и мое мнение таково, что вам нужна только одна форма (и что вводить другое имя для чего-то, у чего уже есть имя, не нужно. и, возможно, сбивает с толку)

5. @ThePointer: Нет, возможно, это не охватывает все ваши вопросы, но там нет ничего, что показалось бы мне неправильным

Ответ №1:

Это называется связанным списком. Указатель next — это то, что позволяет вам просматривать список, поскольку каждый элемент указывает на следующий, а последний next указывает на NULL , чтобы вы знали, что находитесь в конце. Смотрите https://en.wikipedia.org/wiki/Linked_list На самом деле вы сохраняете не структуру, вы сохраняете указатель на следующий элемент, думайте о нем как о массиве, но где вместо выравнивания элементов каждый из них указывает на следующий элемент, используя вместо этого свой адрес.

Что касается next указателя, то причина, по которой он объявлен с ключевым словом struct, заключается в том, что STACKITEM тип еще не существует (он определяется после определения структуры).

Ответ №2:

Типичная структура объявляется следующим образом.

Только если вы никогда не захотите создавать их снова в будущем. Синтаксис, о котором вы спрашиваете, на сегодняшний день является типичным способом объявления структуры.

Typedef позволяет ссылаться на него с помощью более короткого имени struct_format_name вместо struct struct_format_name .

Комментарии:

1. После дальнейших исследований я не думаю, что ваш ответ правильный. Я могу создать другую структуру того же формата, обратившись к ней с помощью ее тега. В данном случае это было бы struct_format_name . Не стесняйтесь поправлять меня.

2. О, извините. Я неправильно понял, что вы сказали. В любом случае, обычной практикой является объявление структуры в заголовочном файле, а затем создание ее экземпляров позже — обычно вы создаете ее одновременно с определением ее структуры, только когда собираетесь создать ее когда-либо. Бит typedef просто избавляет вас от необходимости каждый раз вводить тип «struct struct_type» — вы сохраняете «struct»

3. хорошо, это имеет смысл. Спасибо. Я буду использовать ваши комментарии для дальнейшего использования. 🙂

Ответ №3:

Я понимаю, что такое указатели и структуры. Однако я не понимаю, что значит иметь указатель на структуру внутри структуры. Пожалуйста, проясните и доработайте эту концепцию.

Это обычная реализация C структуры данных, известной как односвязный список, где каждый элемент в списке явно указывает на следующий элемент. В этом случае каждый экземпляр a struct _s указывает на другой экземпляр a struct _s , вот так:

  --- ---           --- ---          --- --- 
|   |   | ----->  |   |   | -----> |   |   |
 --- ---           --- ---          --- --- 
                                     ^   ^
                                     |   |
                                     |    ---- next
                                      -------- value
  

Вы можете реализовать стек, используя структуру связанного списка, что и делает ваш инструктор. Операция «push» будет динамически создавать новую STACKITEM , сохранять в ней целочисленное значение и делать это новое STACKITEM заголовком списка (он же вершина стека). stack Указатель всегда указывает на первый элемент в списке ( NULL если список пуст).

 stack: |||

push( amp;stack, 1 );

        --- --- 
stack: | 1 |   |---|||
        --- --- 
  

Повторные вызовы push добавят новый элемент в начало списка:

 push( amp;stack, 2 );

        --- ---      --- --- 
stack: | 2 |   |--->| 1 |   |---|||
        --- ---      --- --- 

push( amp;stack, 3 );

        --- ---      --- ---      --- --- 
stack: | 3 |   |--->| 2 |   |--->| 1 |   |---|||
        --- ---      --- ---      --- --- 
  

Код будет выглядеть примерно так

 void push( STACKITEM **stack, int value )
{
  STACKITEM *newItem = malloc( sizeof *newItem );
  if ( newItem )
  {
    newItem->value = value;
    newItem->next  = *stack;    // newItem now points to former top of stack
    *stack         = newItem;   // newItem is now the top of the stack
  }
}
  

Операция «pop» работает наоборот — она удаляет первый элемент списка, устанавливая stack указатель так, чтобы он указывал на следующий элемент, и освобождает память этого элемента:

 void pop( STACKITEM **stack )
{
  STACKITEM *top = *stack;
  if ( top )
  {
    *stack = top->next;
    free( top );
  }
}

pop( amp;stack );

        --- ---      --- --- 
stack: | 2 |   |--->| 1 |   |---|||
        --- ---      --- --- 

pop( amp;stack );

        --- --- 
stack: | 1 |   |---|||
        --- --- 

pop( amp;stack );

stack: |||
  

Я не понимаю, почему структура объявлена typedef . Это кажется избыточным по следующим причинам:

typedef служит двум основным целям:

  1. Упрощение имен сложных типов
  2. Абстрагируйтесь от деталей реализации

В этом случае ваш инструктор стремится к 2 — абстрагированию от struct -несности типа элемента стека. К сожалению, если вы как пользователь этого типа должны быть осведомлены о деталях реализации, то это не имеет особого смысла.

ПРИМЕЧАНИЕ: Ваш инструктор не должен использовать начальное подчеркивание в имени тега; идентификаторы с начальными подчеркиваниями зарезервированы для использования реализацией.

Ответ №4:

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

Невозможно определить тип, который может указывать только на определенный объект.

Ваш код и код вашего преподавателя не выполняют одно и то же, поэтому избыточности нет.
Вы определяете как тип, так и объект; они определяют тип и другое имя для этого типа.
(Звучит немного так, как будто вы путаете типы с объектами.)

Они typedef не являются строго необходимыми, но являются очень распространенным способом избежать необходимости вводить struct всякий раз, когда вы используете тип структуры.

И ваш вариант очень aтипичен.
Типы чаще всего определяются глобально, в то время как переменные объявляются как можно более локально.