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

Последнее обновление: 22.01.2024

На структуры, как и на объекты других типов, можно определять указатели. Например, указатель на структуру 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? Рассмотрим следующий пример:

#include 
 
struct 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;
};
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850