Указатель на структуру - формальный параметр
Лабораторная работа 6
СТРУКТУРЫ
Цель работы | Научиться создавать и реализовывать операции над структурами, создавать функции, которые в качестве формальных параметров используют структуру. | Изучить механизмы передачи параметров в фнукциюю : метод копирования и метод передачи параметров с помощью указателей. |
Общие сведения
Предположим, что необходимо сохранить в одном блоке данные, которые содержали бы имя, дату рождения и адрес некоторого пользователя. Массив для этих целей не может использоваться, поскольку все элементы у него должны быть одного типа. Для того, чтобы хранить в памяти множество разнородных данных в языке С используют структурированную переменную (или просто структуру). Пример определения структурированных переменных в С:
struct man // Имя структуры
{ char name[10];
int dd,mm,yy; // Элемент структуры
char *address;
} А, B, X[10]; // Определение структурированных переменных
Составляющие структуру переменные имеют различные типы и имена, по которым они идентифицируются в структуре. Их называют ЭЛЕМЕНТАМИ СТРУКТУРЫ, и они имеют синтаксис определения обычных переменных. Использоваться где-либо еще, кроме как в составе структурированной переменной, они не могут. В данном примере структура состоит из name - массива 10 символов, целых переменных dd, mm и yy и указателя на строку address. После определения элементов структуры следует список структурированных переменных. Каждая из них имеет внутреннюю организацию описанной структуры, то есть полный набор перечисленных элементов. Имя структурированной переменной идентифицирует всю структуру в целом. В данном случае мы имеем переменные А, B и массив X из 10 структурированных переменных.
Имя структуры идентифицирует данную последовательность элементов, поэтому в программе в дальнейшем можно определять как новые структурированные переменные, не раскрывая содержания уже определенной структуры, так и указатели на них:
struct man C,D,*p;
Аналогичным образом формальные параметры функции и ее результат также могут быть указателями на структурированные переменные:
struct man *create() { ... }
void f(struct man *q) { ... }
При определении глобальной (внешней) структурированной переменной или массива таких переменных они могут быть инициализированы списками значений элементов, заключенных в фигурные скобки и перечисленных через запятую:
struct man A = { "Петров",1,10,1969,"Морская-12" };
struct man X[10] =
{ { "Смирнов",12,12,1977,"Дачная-13" },
{ "Иванов" ,21,03,1945,"Северная-21" },
{ .................................. }
};
Способ работы со стуктурированной переменной вытекает из ее аналогии с массивом. Точно так же, как нельзя выполнить операцию над всем массивом, но можно над отдельным его элементом, структуру можно обрабатывать, выделяя отдельные ее элементы. Для этой цели существует операция "."(точка), она выделяет элемент с заданным именем:
A.name // элемент name структурированной переменной A
Если элемент структуры является не простой переменной, а массивом или указателем, то для него применимы соответствующие ему операции ( [],* и адресная арифметика):
A.name[i] // i-й элемент массива name, который является
// элементом структурированной переменной A
*B.address // косвенное обращение по указателю address,
// который является элементом структурированной переменной B
Структура играет особую роль среди всех других способов представления данных. Элементы структуры связаны между собой не только физически (общая память), но и логически, поскольку обычно представляют собой характектеристики и свойства одной сущности или предмета, состояние которого отображается в программе.
Из следующего простого примера видно, каким образом программа работает с элементами из отдельной структурированной переменной A и из массива таких переменных X[10]:
int i;
for (m=0; X[i].name[j]!='\0'; m++)
A.name[m] = “x”.// Работа с отдельными символами элемента name
puts(A.name); // Передача указателя на массив символов name
printf("%d-%d-%d\n",A.yy,A.mm,A.dd);
// Элементы dd,mm,yy - простые переменные
for (i=0; i<10; i++) {
int m;
for (m=0; X[i].name[j]!='\0'; m++)
...X[i].name[m]...// Работа с отдельными символами элемента name
puts(X[i].name); // Передача указателя на массив символов name
printf("%d-%d-%d\n",X[i].yy,X[i].mm,X[i].dd);
// Элементы dd,mm,yy - простые переменные
}
Операция "->"
То, что указатели на структурированные переменные имеют широкое распространение, подтверждается наличием в С специальной операции "->" (стрелка, минус-больше), которая понимается как выделение элемента в структурированной переменной, адресуемой указателем. То есть операндами здесь являются указатель на структуру и элемент структуры. Операция имеет полный аналог в виде сочетания операций "*" и ".":
p->mm эквивалентно .(*p).mm
Указатель на структуру - формальный параметр
В "классическом" С формальными параметрами функции могут быть базовые типы данных и указатели. Передача параметров в функцию связана с их копированием, поэтому транслятор должен уметь это сделать. Но поскольку массивы и структуры нельзя копировать целиком, а только поэлементно, то в формальных параметрах могут быть только указатели на них. Предыдущий пример с использованием указателя на структуру будет выглядеть следующим образом:
void proc(struct man *p) {
...p->name[m]...// Для работы с элементами структуры через указатель
puts(p->name); // на нее используется операция "->"
printf("%d-%d-%d\n",p->yy,p->mm,p->dd);
}
...
int i;
proc(&A);
for (i=0; i<10; i++) proc(&X[i]); // Фактические параметры -
... // указатели на структурированные переменные,
// полученные с помощью операции "&"
В данном случае формальный параметр p считается указателем на отдельную структурированную переменную. Функцию можно несколько изменить, если считать, что указатель ссылается на массив структурированных переменных. Для этого его придется сопроводить еще и размерностью массива, а доступ к элементам структур через этот указатель будет выглядеть несколько иначе:
void proc(struct man *p, int n) { // Здесь p[i].name эквивалентно
int i; // (*(p+i)).name - элемент name
for (i=0; i<n; i++) { // в i-й структурированной переменнной
// от текущего положения указателя
...
puts(p[i].name);
printf("%d-%d-%d\n",p[i].yy,p[i].mm,p[i].dd);
} // Далее фактические параметры -
... // указатели на структурированныеe
proc(&A,1); // переменные (операция "&") и
proc(B,10); // массивы (идентификатор массива)
...