Данные (более детальные сведения)
Структуры
Структура - это конструкция, которая позволяет объединить несколько переменных с разными типами и именами в один составной объект. Она позволяет строить новые типы данных языка Си. В других языках программирования структуры называют записями или кортежами.
Описание структуры выглядит следующим образом:
struct имя_структуры {
описание полей структуры
};
Здесь имя_структуры - это любое имя, соответствующее синтаксису языка Си, описания полей структуры - любая последовательность описаний переменных, имена и типы этих переменных могут быть произвольными. Эти переменные называются полями структуры. Заканчивается описание структуры закрывающей фигурной скобкой. За закрывающей фигурной скобкой в описании структуры обязательно следует точка с запятой.
В качестве примера опишем точку в трехмерном пространстве, который задается тремя вещественными координатами x, y, z:
struct R3Point {
double x;
double y;
double z;
};
Таким образом, вводится новый тип struct R3Point. После того как структура определена, можно описывать переменные такого типа, при этом в качестве имени типа следует использовать выражение struct R3Point. Например, две точки в трехмерном пространстве с именами u, v будут описаны следующим образом:
struct R3Point u, v;
Переменные u,v содержит внутри себя три вещественных поля с именами x, y, z. Для доступа к полям структуры используется операция точка ".". Например, u.x является полем x структуры u, с ним можно работать как с обычной переменной.
В приведенных примерах все поля структуры R3Point имеют один и тот же тип double, однако полями структуры могут быть другие структуры, никаких ограничений нет.
При определении структуры можно выполнить инициализации ее полей:
struct R3Point u={0.1, 0.1, 0.7};
Указатели и структуры
Указатель на структуру S описывается обычным образом, в качестве имени типа фигурирует struct S*. Например, в следующем фрагменте переменная p описана как указатель на структуру S:
struct S { . . . }; // Определение структуры S
struct S *p; // Описание указателя на структуру S
Описание структуры может содержать указатель на структуру того же типа в качестве одного из полей. Язык Си допускает использование указателей на структуры, определение которых еще не завершено. Например, рассмотрим структуру TreeNode (вершина дерева), которая используется при определении бинарного дерева. Она содержит указатели на родительский узел и на левого и правого сыновей, которые также имеют тип struct TreeNode:
struct TreeNode { // Вершина дерева
struct TreeNode *parent; // Указатель на отца,
struct TreeNode *left; // на левого сына,
struct TreeNode *right; // на правого сына
void *value; // Значение в вершине
};
Здесь при описании полей parent, left, right используется тип struct TreeNode * -указатель на структуру TreeNode, определение которой еще не завершено. Возможны и более сложные комбинации.
Для доступа к полям структуры через указатель на структуру служит операция стрелочка, которая обозначается двумя символами −> (минус и знак больше). Пусть S — имя структуры, f — некоторое поле структуры S, p — указатель на структуру S. Тогда выражение p−>f обозначает поле f структуры S. Это выражение можно записать, используя операцию звездочка (доступ к объекту через указатель), p−>f эквивалентно (*p).f. В записи (*p).f круглые скобки вокруг выражения *p обязательны, поскольку приоритет операции точка выше, чем операции звездочка.
3.5.3.Структуры и оператор определения имени типа typedef
Синтаксис языка Си позволяет в одном предложении определить структуру и описать несколько переменных структурного типа. Например, строка
struct R3Point {double x; double y; double z; } t, *p;
одновременно определяет структуру R3Point и описывает две переменные t и p. Первая имеет тип struct R3Point, вторая - struct R3Point *. Таким образом, после закрывающей фигурной скобки может идти необязательный список определяемых переменных, причем можно использовать все конструкции Си для построения сложных типов (указатели, массивы). Список всегда завершается точкой с запятой, поэтому даже при пустом списке точка с запятой после фигурной скобки обязательна.
Возможно анонимное определение структуры, когда имя структуры после ключевого слова struct опускается. В этом случае список описываемых переменных должен быть непустым (иначе такое описание совершенно бессмысленно). Пример:
struct {double x; double y; double z; } t, *p;
Здесь имя структуры отсутствует. Следует отметить, что такие описания программисты обычно не используют, гораздо чаще анонимное определение структуры комбинируют с оператором определения имени типа typedef. Например, можно определить два типа R3Point и R3PointPtr (указатель на точку):
typedef struct {
double x;
double y;
} R3Point, *R3PointPtr;
Такая технология довольно популярна среди программистов и применяется в большинстве системных h-файлов. Преимущество ее состоит в том, что в дальнейшем при описании переменных структурного типа не нужно использовать ключевое слово struct, например,
R3Point a, b, c; // Описываем три точки a, b, c
R3PointPtr p; // Описываем указатель на точку
R3Point *q; // Эквивалентно R3PointPtr q;
Сравните с описаниями, использующими приведенное выше определение структуры R3Point:
struct R3Point a, b, c;
struct R3Point *p;
struct R3Point *q;
Первый способ лаконичнее и нагляднее.
Не обязательно комбинировать оператор typedef непременно с анонимным определением структуры; можно в одном предложении определить имя структуры и ввести новый тип. Например, предложение
typedef struct R3_Point {
double x;
double y;
double z;
} R3Point, *R3Point;
определяет структуру R3_Point, а также два новых типа R3Point (структура R3_Point) и R3PointPtr (указатель на структуру R3_Point). Имя структуры не должно совпадать с именем типа, именно поэтому здесь в качестве имени структуры используется имя R3_Point.
Строки
Строка - это последовательность символов. Специального типа данных строка в Си нет. Строки представляются массивами символов. Последним символом массива, представляющего строку, должен быть символ с нулевым кодом. Пример:
char str[10];
str[0] = 'e'; str[1] = '2';
str[2] = 'e'; str[3] = '4';
str[4] = 0;
Описан массив str из 10 символов, который может представлять строку длиной не более 9, поскольку один элемент должен быть зарезервирован для терминирующего нуля. Далее в массив str записывается строка "e2e4". Строка терминируется нулевым символом. Всего запись строки использует 5 первых элементов массива str с индексами 0...4. Последние 5 элементов массива не используются. Массив можно инициализировать непосредственно при описании, например
char t[] = "abc";
Здесь размер массива t, компилятор вычисляет сам. После операции присваивания записана строковая константа "abc", которая заносится в массив t. В результате компилятор создает массив t из четырех элементов, поскольку на строку отводится 4 байта, включая терминирующий ноль.