На структуры, как и на объекты других типов, можно определять указатели. Например, указатель на структуру person:
struct person *p;
Указатели на структуры можно создавать и для безымянных структурных типов:
struct { int age; char name[20]; } *p1, *p2;
В качестве значения такому указателю присваивается адрес объекта структуры того же типа:
struct person kate = {31, "Kate"}; struct person *p_kate = &kate;
Используя указатель на структуру, можно получить доступ к ее элементам. Для этого можно воспользоваться двумя способами. Первый способ представляет применение операции разыменования:
(*указатель_на_структуру).имя_элемента
Второй способ предполагает использование операции -> (операция стрелка):
указатель_на_структуру->имя_элемента
Используем оба этих способа для обращения к элементам структуры:
#include <stdio.h> struct person { int age; char name[20]; }; int main(void) { struct person kate = {31, "Kate"}; // указатель на переменную kate struct person * p_kate = &kate; // получаем значение элемента name // получаем значение элемента age char * name = p_kate->name; int age = (*p_kate).age; printf("name = %s \t age = %d \n", name, age); // изменим элемент age в структуре p_kate->age = 32; printf("name = %s \t age = %d \n", kate.name, kate.age); return 0; }
Здесь определяется указатель p_kate на переменную kate. И используя указатель, мы можем получить или изменить значения элементов структуры.
Элементы структуры могут представлять указатель на структуру этого же типа. Подобная ситуация встречается довольно часто в различных типах данных, которые состоят из связанных звеньев, например, в связных списках, где каждый элемент имеет указатель на следующий и/или предыдущий элемент этого же типа структуры. Например:
struct node { char* value; // значение struct node* next; // указатель на следующий узел };
Структура node
имеет два элемента. Элемент value
хранит собственно некоторое значение. А элемент next
представляет указатель на следующий объект
структуры node. Как мы можем использовать эту структуру и зачем нам указатель на следующий объект node? Рассмотрим следующий пример:
#includestruct node { char* value; struct node* next; }; int main(void) { struct node kate = {.value = "Kate"}; struct node tom = {.value = "Tom" }; struct node bob = {.value = "Bob"}; kate.next = &tom; // Kate - Tom tom.next = &bob; // Tom - Bob // устанавливаем указатель на первую структуру в цепочке struct node * pointer = &kate; while(pointer != NULL) { printf("value = %s \n", pointer->value); pointer = pointer->next; // переходим к следующему объекту } return 0; }
Здесь определяем три переменных структуры: kate, tom и bob. У объекта kate элемент next указывает на объект tom:
kate.next = &tom;
А у объекта tom элемент next указывает на объект bob:
tom.next = &bob;
Таким образом, фактически мы получим цепочку Kate - Tom - Bob
или грубо говоря список объектов структуры node.
Используя эти указатели, мы можем перемещаться вперед по этому списку. Для этого сначала устанавливаем указатель на первую структуру:
struct node * pointer = &kate;
Далее в цикле while пока указатель pointer не будет указать ни на какой объект структуры (то есть будет равен NULL
) выводим значение value
текущего объекта, на который указывает pointer, и затем присваиваем ему значение указателя pointer->next
, то есть переходим к следуюшей структуре в списке:
while(pointer != NULL) { printf("value = %s \n", pointer->value); pointer = pointer->next; }
Таким образом, можно определять связанные наборы объектов, не прибегая к массивам.
Указатели на структуры могут быть полезны в том случае, если определение одной структуры идет после ее использования. Например:
struct rectangle { struct color background; unsigned width; unsigned height; }; struct color { unsigned char red; unsigned char green; unsigned char blue; unsigned char alpha; };
Здесь структура rectangle (прямоугольник) определяет поле, которое представляет структуру color (фоновый цвет). Но структура color определена после структуры rectangle, поэтому при компиляции мы получим ошибку:
error: field 'background' has incomplete type
Как говорит ошибка, тип поля background - структура color является неполным. С одной стороны, мы могли бы объявить структуру color до структуры rectangle, но в комплексных программах из множества файлов может быть сложно проследить весь порядок определения и использования типов. С другой стороны, возможно потребуется определить структуры, которые рекурсивно ссылаются друг на друга, наподобие следующего:
struct A { struct B b; // Ошибка - incomplete type } struct B { struct A a; }
В этом случае мы можем вначале объявить структуру (не указывая определение ее полей), а при использовании применять указатель на структуру:
#include <stdio.h> struct color; // объявление структуры struct rectangle { struct color* background; // указатель на структур unsigned width; unsigned height; }; struct color // определение структуры { unsigned char red; unsigned char green; unsigned char blue; unsigned char alpha; }; int main(void) { // пример использования struct color backgroundColor = {255, 0, 0, 125}; struct rectangle rect = {.width=100, .height=50, .background = &backgroundColor}; printf("Rectangle color: %d, %d, %d", rect.background->red, rect.background->blue, rect.background->green); return 0; }
Аналогично с рекурсивными структурами:
struct B; // объявление struct A { struct B* b; // указатель на структуру }; struct B // определение { struct A* a; };