Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс ПЯВУ 2 сем / Лекции 2 сем / Л№23.Структуры.ООП / Примеры задания структур .odt
Скачиваний:
11
Добавлен:
17.04.2015
Размер:
91.18 Кб
Скачать

Структуры в си

Структура – это сложный тип данных представляющий собой упорядоченное в памяти множество элементов различного типа. Каждый элемент в структуре имеет свое имя и называется полем. Элементы в структуре располагаются последовательно. Размер структуры определяется суммой размеров всех элементов.

Объявление структуры имеет вид:

struct [имя типа] {

поле №1;

поле №2;

...

поле №N;

} [список переменных];

Объявление полей структуры возможно только без инициализации. Если несколько полей следующих друг за другом в описании структуры имеют один и тот же тип, то для их описания можно использовать синтаксис объявления нескольких переменных одного и того же типа. Типом поля может быть любой тип (как системный, так и пользовательский), описанный ранее.

Примеры структур

Структура в си, содержащая информацию о точке в двумерном пространстве (координаты):

struct Point{

double x, y;

};

Структура в си, содержащая информацию об окружности (координаты центра и радиус):

struct Circle{

double x, y, radius;

};

Структура в си, содержащая информацию о студенте (фамилия, имя, отчество, номер зачетной книжки, средний балл):

struct Student{

char surname[15], name[15], patronymic[15];

unsigned number;

double rate;

};

Структура в си, содержащая информацию о группе студентов (название группы, количество студентов, список студентов (максимально 30)):

struct Group{

char name[10];

unsigned number;

struct Student list[30];

};

Объявление переменной определенной структуры осуществляется после описания данной структуры в следующем виде:

struct тип имя №1[= значение №1][,...];

struct Point pnt[3] = {{0,0},{1,0},{0,1}};

struct Circle c1 = {10.0,10.0,5.0},

c2 = {0.0,0.0,25.0};

struct Student st

= {"Иванов”,”Иван”,”Иванович”,959623,7.5};

struct Group gr ={

"97-BC”, 3, {

{"Иванов”, ”Иван”, ”Иванович”, 979601,8.0},

{"Петров”, ”Петр”, ”Петрович”, 979602,6.5},

{"Сидоров”,”Сидор”,”Сидорович”,979603,9.0}

}

};

Ввод информации о студенте в переменную st типа Student:

scanf("%s %s %s %u %lf”, &st.surname, &st.name,

&st.patronymic, &st.number, &st.rate);

Вывод на экран списка группы, заданной в переменой gr типа Group:

printf("Группа: %s\n”,gr.name);

for(unsigned i=0;i printf("%2u: %15s %15s %15s %6u %.1lf\n”, i+1, gr.list[i].surname, gr.list[i].name, gr.list[i].patronymic, gr.list[i].number, gr.list[i].rate);

Для определения размера переменной структурного типа в байтах используется оператор определения типа sizeof. Например:

unsigned size = sizeof(struct Student); //size == 57

Структура — это совокупность переменных, объединенных под одним именем. С помощью структур удобно размещать в смежных полях связанные между собой элементы информации. Объявление структуры создает шаблон, который можно использовать для создания ее объектов (то есть экземпляров этой структуры). Переменные, из которых состоит структура, называются членами. (Члены структуры еще называются элементами или полями.)

Как правило, члены структуры связаны друг с другом по смыслу. Например, элемент списка рассылки, состоящий из имени и адреса логично представить в виде структуры. В следующем фрагменте кода показано, как объявить структуру, в которой определены поля имени и адреса. Ключевое слово struct сообщает компилятору, что объявляется (еще говорят, "декларируется") структура.

struct addr

{

char name[30];

char street[40];

char city[20];

char state[3];

unsigned long int zip;

};

Обратите внимание, что объявление завершается точкой с запятой, потому что объявление структуры является оператором. Кроме того, тег структуры addr идентифицирует эту конкретную структуру данных и является спецификатором ее типа.

В данном случае на самом деле никакая переменная не создается. Всего лишь определяется вид данных. Когда вы объявляете структуру, то определяете агрегатный тип, а не переменную. И пока вы не объявите переменную этого типа, то существовать она не будет. Чтобы объявить переменную (то есть физический объект) типа addr, напишите следующее:

struct addr addr_info;

В этом операторе объявлена переменная типа addr, которая называется addr_info. Таким образом, addr описывает вид структуры (ее тип), a addr_info является экземпляром (объектом) этой структуры.

Когда объявляется переменная-структура, компилятор автоматически выделяет количество памяти, достаточное, чтобы разместить все ее члены. На рис. 7.1 показано, как addr_info размещена в памяти; в данном случае предполагается, что целые переменные типа long занимают по 4 байта.+------------------------------------------+

|Name (имя) 30 байт |

+------------------------------------------+

+-------------------------------------------------+

|Street (улица) 40 байт |

+-------------------------------------------------+

+-----------------------------------+

|City (город) 20 байт |

+-----------------------------------+

+---------------------+

|State (штат) 3 байта |

+---------------------+

+----------------------------+

|Zip (код) 4 байта |

+----------------------------+

Рис. 7.1. Расположение в памяти структуры addr_info

Одновременно с объявлением структуры можно объявить одну или несколько переменных. Например,

struct addr {

char name[30];

char street[40];

char city[20];

char state[3];

unsigned long int zip;

} addr_info, binfo, cinfo;

определяет тип структуры, называемый addr, и объявляет переменные этого типа addr_info, binfo и cinfo. Важно понимать, что каждая переменная-структура содержит собственные копии членов структуры. Например, поле zip в binfo отличается от поля zip в cinfo. Изменения в zip из binfo не повлияют на содержимое поля zip, находящегося в cinfo.

Если нужна только одна переменная-структура, то тег структуры является лишним. В этом случае наш пример объявления можно переписать следующим образом:

struct {

char name[30];

char street[40];

char city[20];

char state[3];

unsigned long int zip;

} addr_info;

В этом случае объявляется одна переменная с именем addr_info, причем ее поля указаны в структуре, которая предшествует этому имени.

Общий вид объявления структуры такой:

struct тег {

тип имя-члена;

тип имя-члена;

тип имя-члена;

.

.

.

} переменные-структуры;

причем тег или переменные-структуры могут быть пропущены, но только не оба одновременно.

Доступ к членам структуры

Доступ к отдельным членам структуры осуществляется с помощью оператора . (который обычно называют оператором точка или оператором доступа к члену структуры). Например, в следующем выражении полю zip в уже объявленной переменной-структуре addr_info присваивается значение ZIP-кода, равное 12345:

addr_info.zip = 12345;

Этот отдельный член определяется именем объекта (в данном случае addr_info), за которым следует точка, а затем именем самого этого члена (в данном случае zip). В общем виде использование оператора точка для доступа к члену структуры выглядит таким образом:

имя-объекта.имя-члена

Поэтому, чтобы вывести ZIP-код на экран, напишите следующее:

printf("%d", addr_info.zip);

Будет выведен ZIP-код, который находится в члене zip переменной-структуры addr_infо.

Точно так же в вызове gets() можно использовать массив символов addr_infо.name:

gets(addr_info.name);

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

Так как name является массивом символов, то чтобы получить доступ к отдельным символам в массиве addr_info.name, можно использовать индексы вместе с name. Например, с помощью следующего кода можно посимвольно вывести на экран содержимое addr_info.name:

for(t=0; addr_info.name[t]; ++t)

putchar(addr_info.name[t]);

Обратите внимание, что индексируется именно name (а не addr_info). Помните, что addr_info — это имя всего объекта-структуры, a name — имя элемента этой структуры. Таким образом, если требуется индексировать элемент структуры, то индекс необходимо указывать после имени этого элемента.

Присваивание структур

Информация, которая находится в одной структуре, может быть присвоена другой структуре того же типа при помощи единственного оператора присваивания. Нет необходимости присваивать значения каждого члена в отдельности. Как выполняется присваивание структур, показывает следующая программа:

#include <stdio.h>

int main(void)

{

struct {

int a;

int b;

} x, y;

x.a = 10;

y = x; /* присваение одной структуры другой */

printf("%d", y.a);

return 0;

}

После присвоения в y.a будет храниться значение 10.

Структуры

Структура - это объединение одного или нескольких объектов (переменных, массивов, указателей, других структур и т.д.). Как и массив, она представляет собой совокупность данных. Отличием является то, что к ее элементам необходимо обращаться по имени и что различные элементы структуры не обязательно должны принадлежать одному типу.

Объявление структуры осуществляется с помощью ключевого слова struct, за которым идет ее тип и далее список элементов, заключенных в фигурные скобки:

struct тип { тип элемента_1 имя элемента_1;

.........

тип элемента_n имя элемента_n;

};

Именем элемента может быть любой идентификатор. Как и выше, в одной строке можно записывать через запятую несколько идентификаторов одного типа.

Рассмотрим пример:

sruct date { int day;

int month;

int year;

};

Следом за фигурной скобкой, заканчивающей список элементов, могут записываться переменные данного типа, например:

struct date {...} a, b, c;

(при этом выделяется соответствующая память). Описание без последующего списка не выделяет никакой памяти; оно просто задает форму структуры. Введенное имя типа позже можно использовать для объявления структуры, например:

struct date days;

Теперь переменная days имеет тип date.

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

Разрешается вкладывать структуры друг в друга, например:

struct man { char name[20], fam[20];

struct date bd;

int age;

};

Определенный выше тип data включает три элемента: day, month, year, содержащий целые значения (int). Структура man включает элементы name, fam, bd и voz. Первые два - name[20] и fam[20] - это символьные массивы из 20 элементов каждый. Переменная bd представлена составным элементом (вложенной структурой) типа data. Элемент age содержит значения целого типа int). Теперь можно определить переменные, значения которых принадлежат введенному типу:

struct man man_[100];

Здесь определен массив man_, состоящий из 100 структур типа man.

Чтобы обратиться к отдельному элементу структуры, необходимо указать его имя, поставить точку и сразу же за ней записать имя нужного элемента, например:

man_[j].age = 19;

man_[j].bd.day = 24;

man_[j].bd.month = 2

man_[j].bd.year = 1987;

При работе со структурами необходимо помнить, что тип элемента определяется соответствующей строкой описания в фигурных скобках. Например, массив man_ имеет тип man, year является целым числом и т.п. Поскольку каждый элемент структуры относится к определенному типу, его имя может появиться везде, где разрешено использование значений этого типа. Допускаются конструкции вида man_[i]=man_[j]; где man_[i] и man_[j] - объекты, соответствующие единому описанию структуры. Другими словами, разрешается присваивать одну структуру другой по их именам.

Унарная операция & позволяет взять адрес структуры. Предположим, что определена переменная day:

struct date {int d, m, у;} day;

Здесь day - это структура типа date, включающая три элемента: d, m, у. Другое определение

struct date *db;

устанавливает тот факт, что db - это указатель на структуру типа date.

Запишем выражение:

db = &day;

В этом случае для выбора элементов d, m, у структуры необходимо использовать конструкции:

(*db).d; (*db).m; (*db).y;

Действительно, db - это адрес структуры, *db - сама структура. Круглые скобки здесь необходимы, так как точка имеет более высокий, чем звездочка, приоритет. Для аналогичных целей в языке Си предусмотрена специальная операция ->. Эта операция выбирает элемент структуры и позволяет представить рассмотренные выше конструкции в более простом виде:

db -> d; db -> m; db -> у;

Оператор typedef

Рассмотрим описание структуры:

struct data {int d, m, у;};

Здесь фактически вводится новый тип данных - data. Теперь его можно использовать для объявления конкретных экземпляров структуры, например:

struct data а, b, с;

В язык Си введено специальное средство, позволяющее назначать имена типам данных (переименовывать). Таким средством является оператор typedef. Он записывается в следующем виде:

typedef тип имя;

Здесь "тип" - любой разрешенный тип данных и "имя" - любой разрешенный идентификатор.

Рассмотрим пример:

typedef int INTEGER;

После этого можно сделать объявление:

INTEGER а, b;

Оно будет выполнять то же самое, что и привычное объявление int a,b;. Другими словами, INTEGER можно использовать как

синоним ключевого слова int.

Битовые поля

Особую разновидность структур представляют собой битовые поля. Битовое поле - это последовательность соседних битов внутри одного, целого значения. Оно может иметь тип signed int или unsigned int и занимать от 1 до 16 битов. Поля размещаются в машинном слове в направлении от младших к старшим разрядам. Например, структура:

struct prim { int a:2;

unsigned b:3;

int c:5;

int d:1;

unsigned d:5; } i, j;

обеспечивает размещение данных в двух байтах (в одном слове). Если бы последнее поле было задано так: unsigned d:6, то оно размещалось бы не в первом слове, а в разрядах 0 - 5 второго слова.

В полях типа signed крайний левый бит является знаковым.

Поля используются для упаковки значений нескольких переменных в одно машинное слово с целью экономии памяти. Они не могут

быть массивами и не имеют адресов, поэтому к ним нельзя применять унарную операцию &.

Объединение (union)

Объединение - это некоторая переменная, которая может хранить (в разное время) объекты различного типа и размера. В результате появляется возможность работы в одной и той же области памяти с данными различного вида. Для описания объединения используется ключевое слово union, а соответствующий синтаксис аналогичен структурам.

Пусть задано определение:

union r {int ir; float fr; char cr;} z;

Здесь ir имеет размер 2 байта, fr - 4 байта, cr - 1 байт. Размер переменной z будет равен размеру самого большого из трех приведенных

типов (т.е. 4 байтам). В один и тот же момент времени z может иметь значение только одной из переменных ir, fr или cr.

Передача структур в функции.

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

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

#include <stdio.h>

#include <string.h>

struct month

{

char *name;

char *abbreviation;

int days;

} month_details[] =

{

"Junk", "Junk", 0,

"January", "Jan", 31,

"February", "Feb", 28,

"March", "Mar", 31,

"April", "Apr", 30,

"May", "May", 31,

"June", "Jun", 30,

"July", "Jul", 31,

"August", "Aug", 31,

"September", "Sep", 30,

"October", "Oct", 31,

"November", "Nov", 30,

"December", "Dec", 31

};

struct date

{

int day;

int month;

int year;

};

int isLeapYear(struct date d);

int main()

{

struct date d;

printf("Enter the date (eg: 11/11/1980): ");

scanf("%d/%d/%d", &d.day, &d.month, &d.year);

printf("The date %d %s %d is ", d.day,

month_details[d.month].name, d.year);

if (isLeapYear(d) == 0)

printf("not ");

puts("a leap year");

return 0;

}

int isLeapYear(struct date d)

{

if ((d.year % 4 == 0 && d.year % 100 != 0) ||

d.year % 400 == 0)

return 1;

return 0;

}

Присваивание структур.

5. Структуры данных.

Структуры ("записи") представляют собой агрегаты разнородных данных (полей разного типа); в отличие от массивов, где все элементы имеют один и тот же тип.

struct {

int x, y; /* два целых поля */

char s[10]; /* и одно - для строки */

} s1;

Структурный тип может иметь имя:

struct XYS {

int x, y; /* два целых поля */

char str[10]; /* и одно - для строки */

};

Здесь мы объявили тип, но не отвели ни одной переменной этого типа (хотя могли бы). Теперь опишем переменную этого типа и указатель на нее:

struct XYS s2, *sptr = &s2;

Доступ к полям структуры производится по имени поля (а не по индексу, как у массивов):

имя_структурной_переменной.имя_поля

указатель_на_структуру -> имя_поля

то есть

не а

#define ВЕС 0 struct { int вес, рост; } x;

#define РОСТ 1 x.рост = 175;

int x[2]; x[РОСТ] = 175;

Например

s1.x = 13;

strcpy(s2.str, "Finish");

sptr->y = 27;

Структура может содержать структуры другого типа в качестве полей:

struct XYS_Z {

struct XYS xys;

int z;

} a1;

a1.xys.x = 71; a1.z = 12;

Структура того же самого типа не может содержаться в качестве поля - рекурсивные определения запрещены. Зато нередко используются поля - ссылки на структуры такого же типа (или другого). Это позволяет организовывать списки структур:

struct node {

int value;

struct node *next;

};

Очень часто используются массивы структур:

struct XYS array[20]; int i = 5, j;

array[i].x = 12;

j = array[i].x;

Статические структуры можно описывать с инициализацией, перечисляя значения их полей в {} через запятую:

extern struct node n2;

struct node n1 = { 1, &n2 },

n2 = { 2, &n1 },

n3 = { 3, NULL };

В этом примере n2 описано предварительно для того, чтобы &n2 в строке инициализации n1 было определено.

Структуры одинакового типа можно присваивать целиком (что соответствует присваиванию каждого из полей):

struct XYS s1, s2; ...

s2 = s1;

в отличие от массивов, которые присваивать целиком нельзя:

int a[5], b[5]; a = b; /* ОШИБОЧНО ! */

Пример обращения к полям структуры:

typedef struct _Point {

short x, y; /* координаты точки */

char *s; /* метка точки */

} Point;

Point p; Point *pptr; short *iptr;

struct _Curve {

Point points[25]; /* вершины ломанной */

int color; /* цвет линии */

} aLine[10], *linePtr = & aLine[0];

...

pptr = &p; /* указатель на структуру p */

p.x = 1; p.y = 2; p.s = "Grue";

linePtr->points[2].x = 54; aLine[5].points[0].y = 17;

В ы р а ж е н и е значение

---------+------------+------------+-----------+----------

p.x | pptr->x | (*pptr).x | (&p)->x | 1

---------+------------+------------+-----------+----------

&p->x | ошибка

-----------+----------------+------------------+----------

iptr= &p.x | iptr= &pptr->x | iptr= &(pptr->x) | адрес поля

-----------+----------------+--------+---------+----------

*pptr->s | *(pptr->s) | *p.s | p.s[0] | 'G'

-----------+----------------+--------+---------+----------

pptr->s[1] | (&p)->s[1] | p.s[1] | 'r'

-----------+----------------+------------------+----------

&p->s[1] | ошибка

-----------+----------------+------------------+----------

(*pptr).s | pptr->s | p.s | "Grue"

-----------+----------------+------------------+----------

*pptr.s | ошибка

-----------------------------------------------+----------

Вообще (&p)->field = p.field

pptr->field = (*pptr).field

Объединения - это агрегаты данных, которые могут хранить в себе значения данных разных типов на одном и том же месте.

struct a{ int x, y; char *s; } A;

union b{ int i; char *s; struct a aa; } B;

Структура:

________________________

A: | A.x int | Три поля

------------------------ расположены подряд.

| A.y int | Получается как бы

------------------------ "карточка" с графами.

| A.s char * |

-----------------------

А у объединений поля расположены "параллельно",

на одном месте в памяти.

_______________________________________________________

B: | B.i int | B.s char * | B.aa : B.aa.x int |

-----------| | struct a : B.aa.y int |

---------------| : B.aa.s char * |

|___________________________|

Это как бы "ящик" в который можно поместить значение любого типа из перечисленных, но не ВСЕ ВМЕСТЕ ("и то и это", как у структур), а ПО ОЧЕРЕДИ ("или/или"). Размер его достаточно велик, чтоб вместить самый большой из перечисленных типов данных.

Мы можем занести в union значение и интерпретировать его как другой тип данных это иногда используется в машинно-зависимых программах. Вот пример, выясняющий порядок байтов в short числах:

union lb {

char s[2]; short i;

} x;

unsigned hi, lo;

x.i = (02 << 8) | 01;

hi = x.s[1]; lo = x.s[0];

printf( "%d %d\n", hi, lo);

или так:

#include <stdio.h>

union {

int i;

unsigned char s[sizeof(int)];

} u;

void main(){

unsigned char *p;

int n;

u.i = 0x12345678;

for(n=0, p=u.s; n < sizeof(int); n++, p++){

printf("%02X ", *p);

}

putchar('\n');

}

или порядок слов в long числах:

union xx {

long l;

struct ab {

short a; /* low word */

short b; /* high word */

} ab;

} c;

main(){ /* На IBM PC 80386 печатает 00020001 */

c.ab.a = 1; c.ab.b = 2; printf("%08lx\n", c.l );

}

531. Структуры

Мы уже знаем, что массив - это переменная, которая дает возможность хранить в программе множество значений одного и того же типа. Другими словами, массив дает возможность сгруппировать связанную информацию в одном имени, например, рейтинг 100 человек или зарплата 50 служащих. По мере усложнения программы возникает необходимость группировать связанную информацию различных типов данных. Например, предположим, что программа работает с информацией о служащих. Для каждого служащего необходимо поддерживать следующие данные:

char name[64]; // Фамилия

int age; // Возраст

char ssan[ll]; // Социальный номер служащего

int pay_grade; // Разряд по оплате

float salary; // Зарплата

unsigned employee_number; // Номер служащего

Предположим, что в программе есть несколько различных функций, которые работают с этой информацией. Каждый раз при вызове любой из этих функций необходимо следить за тем, что ей переданы все параметры, причем в указанном порядке. Как обсуждалось в разделе "Функции", чем больше у функции параметров, тем труднее программа для восприятия и больше вероятность ошибки. Для упрощения программа может создать структуру, в которой связанная информация группируется под одним именем. Например, поля сведений о служащих можно сгруппировать в структуру Employee, объявление которой имеет вид:

struct Employee {

char name[64]; // Фамилия

int age; // Возраст

char ssan[ll]; // Социальный номер служащего

int pay_grade; // Разряд по оплате

float salary; // Зарплата

unsigned employee_number; // Номер служащего

};

Описание такого рода создает структуру типа Employee.

532. Структура как шаблон для объявления переменных

В С531 обсуждалась возможность языка Си для группирования связанной информации в структуру. Само по себе объявление структуры не создает никаких переменных. Структура есть шаблон, который программа может использовать в дальнейшем для объявления переменных. Таким образом, объявление структуры само по себе не приводит к выделению памяти. Это объявление используется компилятором только в том случае, если в программе будет объявлена переменная типа структура.

533. Тег или имя структуры

В С531 обсуждалась возможность языка Си для группирования связанных данных в структуру. Используя ключевое слово struct, программа может объявить структуру следующим образом:

struct Employee {

char name[64]; // Фамилия

int age; // Возраст

char ssan[ll]; // Социальный номер служащего

int pay_grade; // Разряд по оплате

float salary; // Зарплата

unsigned employee_number; // Номер служащего };

};

В данном случае именем структуры является идентификатор Employee. Программисты-пользователи языка Си называют имя структуры тегом структуры. Как мы увидим в дальнейшем, тег структуры используется для объявления переменной указанного типа. Следующее объявление создает структуру с именем Shape:

struct Shape {

int type; // 0 - окружность, 1 - квадрат, 2 - треугольник

int color;

float radius;

float area;

float perimeter;

};

534. Различные способы объявления переменных типа структура

Итак, язык Си дает возможность группировать связанные данные в структуру (С531). Само по себе объявление структуры не создает переменной, а служит шаблоном для ее объявления. Описать переменную можно двумя способами. Первый способ, пусть в программе объявлена структура типа Employee:

struct Employee {

char name[64]; //Фамилия

int age; //Возраст

char asan[ll]; //Социальный номер служащего

int pay_grade; //Разряд по оплате

float salary; //Зарплата

unsigned employee number; //Номер служащего

Следуя представленному определению структуры, можно объявить переменную типа Employee:

struct Employee employee_info;

struct Employee new_employee, terminated_employee;

Второй способ, объявление переменной может непосредственно следовать за объявлением структуры:

struct Employee {

char name[64]; // Фамилия

int age; // Возраст

char ssan[ll]; // Социальный номер служащего

int pay_grade; // Разряд по оплате

float salary; // Зарплата

unsigned employee_number; // Номер служащего

} employee_info, new_employee,terminated_employee;

535. Члены структуры

Как уже известно, Си дает возможность группировать связанные данные в структуру. Например, в следующем операторе создается переменная triangle с использованием структуры типа Shape:

struct Shape {

int type; // 0 - окружность, 1 - квадрат, 2 - треугольник

int color;

float radius;

float area;

float perimeter;

} triangle;

Каждый выделенный элемент информации структуры представляет собой отдельный член этой структуры. Структура Shape состоит из 5 членов: type, color, radius, area и perimeter. Для обращения к члену структуры используется операция "точка"(.). Например, в следующем фрагменте членам структуры присваиваются конкретные значения:

triangle.type = 2;

triangle.perimeter = 30.0;

triangle.area = 45.0;

536. Распределение памяти под структуры

При объявлении переменной типа заданной структуры выделяется память для всех членов структуры. Например, при объявлении переменной типа Employee с объявлением структуры из С535 выделяет память, как показано на рис. 536.

Рис. 536. Распределение памяти под структуру на примере Employee

537. Адрес структуры как фактический параметр функции

Как известно, язык Си позволяет группировать информацию в виде структуры. В разделе "Дата и время" используется функция geldate для определения текущей системной даты. Функция присваивает текущую дату членам структуры типа date:

struct date {

int da_year // Год char

da_day // День

char da_mon // Месяц

};

В следующей программе DOSDATE.C функция getdate используется для присваивания переменной cwr_date текущей системной даты:

#include <stdio.h>

#include <dos.h>

void main (void)

{

struct date curr_date;

getdate(&curr_date);

printf("Текущая системная дата: %d-%d-%d\n", curr date.da mon,

curr_date.da day, curr_date.da_year);

}

Поскольку функция должна изменить значение параметра, программа передает переменную типа "структура" по ссылке (адресу).

538. Передача структуры по значению как фактического параметра функции

В языке Си переменные типа заданной структуры могут передаваться в качестве параметров функций, как и любые другие переменные. В следующей программе STRUFUNC.C функция show_structure принимает в качестве параметра переменную типа структуры Shape. Функция show_struclure выводит на экран значения всех членов структуры:

#include <stdio.h>

struct Shape {

int type;

int color;

float radius;

float area;

float perimeter;

};

void show structure(struct Shape shape)

{

printf("shape.type %d\n", shape.type);

rintf("shape.color %d\n", shape.color);

rintf("shape.radius %f shape.area %f shape.perimeter %f\n",

hape.radius, shape.area, shape.perimeter);

}

void main(void)

{

struct Shape circle;

circle.type = 0;

circle.color = 1;

circle.radius = 5.0;

circle.area == 22.0 / 7.0 * circle.radius * circle.radius;

circle.perimeter = 2.0* 22.0 / 7.0 * circle.radius;

show structure(circle);

}

539. Изменение структуры внутри функции

В С538 была продемонстрирована возможность передачи переменной типа структура как параметра функции так же, как и для переменных любого типа. Если оказывается необходимым изменять значения членов структуры, то переменная должна передаваться не по значению, а по адресу (так же, как и переменная любого другого типа, значение которой требуется изменить). В следующей программе CHGSTRUC.C определяется функция change„structure, которая изменяет значения членов структуры типа Shape:

#include <stdio.h>

Struct Shape

{

int type;

int color;

float radius;

float area;

float perimeter;

);

void change structure(struct Shape * shape)

{

(*shape)-type = 0;

(*shape).color = 1;

(*shape).radius = 5.0;

(*shape).area = 22.0 / 7.0 * (*shape).radius * (*shape).radius;

(*shape).perimeter = 2.0 * 22.0 / 7.0 * (*shape).radius;

}

void main(void)

{

struct Shape circle;

change_structure(&circle);

printf("circle.type %d\n", circle.type);

printf("circle.color %d\n", circle.color);

printf("circle.radius %f circle.area %f circle.perimeter %f\n",

circle.radius, circle.area, circle.perimeter);

}

Для передачи структуры как параметра функции используется указатель на переменную типа структура. Внутри функции доступ к членам структуры осуществляется посредством операции "звездочка" (*) -извлечения значения по указателю:

(*pointer).member = value;

Эта операция рассматривается в С540.

540. Операция (*указатель).член

Если в функции необходимо изменять значения переменной типа структура, то программа должна передавать указатель на эту переменную. Внутри функции содержимое по этому указателю определяется с помощью операции "звездочка" (*):

(*pointer).member = value;

В этом выражении сначала выполняется операция в скобках - "звездочка" (*), которая определяет адрес в памяти, отведенной для переменной типа структура. По имени члена компилятор Си добавляет к адресу смещение. Если скобки не указаны:

*pointer.member = value;

Си предполагает, что член структуры сам является указателем и операция "звездочка" (*) применяется к члену структуры. Такой синтаксис может быть корректен в том случае, если член структуры объявлен как указатель, например:

struct Planet {

char name[48];

int *some_pointer;

} planet;

Как видно, второй член структуры является указателем на значение типа int. Предполагая, что указатель был присвоен некоторому адресу памяти, следующий оператор заносит по этому адресу значение 5:

*planet.some pointer = 5;

541. Формат указатель->член

В С540 мы видели, что если в функции необходимо изменять значения переменной типа структура, то программа должна передавать указатель на эту переменную. Для нахождения содержимого адреса внутри функции Си поддерживает два формата. Первый формат был использован в С540 - обращение к члену структуры следующим образом:

(*pointer).member = value;

some_value = (*pointer).member;

Второй формат имеет вид:

pointer->member = valued;

some_value = pointer->member;

В следующей программе CHMEMBER.C функция change Jstructure использует второй формат для обращения к членам структуры, передаваемой функции по адресу.

#include <stdio.h>

struct Shape {

int type;

int colors;

float radius;

float area;

float perimeter;

};

void change_structure(struct Shape *shape)

{

shape->type = 0;

shape->color = 1;

shape->radius = 5.0;

shape->area = 22.0 / 7.0 * shape->radius * shape->radius;

shape->perimeter =2.0 * 22.0 / 7.0 * shape->radius;

}

void main(void)

{

struct Shape circle;

change_structure(&circle);

printf("circle.type %d\n", circle.type);

printf("circle.color %d\n", circle.color);

printf("circle.radius %f circle.area %f circle.perimeter %f\n",

circle.radius, circle.area, circle.perimeter);

}

542. Структура без тега

Тег структуры - это имя структуры. Тег структуры используется при объявлении переменной типа этой структуры. Однако при объявлении переменной типа структура тег указывать необязательно. Например, в следующем фрагменте объявлены две переменные типа структуры:

struct {

int type; // 0 - окружность, 1 - квадрат, 2 - треугольник

int colors;

float radius;

float area;

float perimeter;

} triangle, circle;

Если в программе не требуется обращения к структуре по имени (например, в прототипе функции или формальном параметре), то тег структуры можно не указывать. Однако наличие тега дает возможность другим программистам, читающим вашу программу, понять назначение структуры. Включение содержательных имен структур делает программу более удобочитаемой.

543. Область видимости объявлений структуры

В разделе "Функции" понятие область видимости раскрывалось как область программы, в которой объявление идентификатора (будь то переменная или функция) является доступным. При определении структуры необходимо иметь в виду область ее видимости. Можно было обратить внимание на то, что в программах, которые работают со структурами и функциями, объявления структур предшествуют объявлениям функций. В результате объявления структуры имеют глобальную область видимости, что дает возможность любой функции.обращаться к нему. Если структура объявлена внутри main, то единственной функцией, в которой доступно это объявление, является main. Если необходимо использовать объявление структуры в нескольких функциях, структура должна быть объявлена вне функций.

544. Инициализация структуры

Подобно массивам, Си дает возможность инициализировать структуры непосредственно при их объявлении. В следующей программе INITSTRU.C объявляется и инициализируется переменная типа структуры Shape:

#include <stdio.h>

void main(void)

{

struct Shape {

int type;

int color;

float radius;

float area;

float perimeter;

} circle = {0, 1, 5.0, 78.37, 31.42);

printf("circle.type %d\n", circle.type);

printf("circle.color %d\n", circle.color)

printf("circle.radius %f circle.area %f circle.perimeter %f\n",

circle.radius, circle.area, circle.perimeter);

}

Поскольку структура используется только в функции main, она объявлена внутри main.

545. Ввод-вывод переменных типа структура

В приведенных примерах для вывода значений одного или нескольких членов структуры использовалась функция printf. При выполнении операций вывода на экран или ввода с клавиатуры, изменяющих члены структуры, для каждого члена необходимо выполнить отдельную операцию ввода-вывода. Однако при чтении структуры из файла или записи в файл можно работать со всей структурой. Для чтения и записи структур через файловый поток можно использовать функции fread и fwrite. В разделе "Файлы, директории и дисковые устройства" демонстрируется использование этих функций для ввода-вывода структур. При выполнении операций ввод-вывода структур с использованием идентификатора файла применяются функции read и write. Для компилятора Си структура, с точки зрения ее хранения в памяти, - это просто массив байтов. Все упомянутые функции ввода-вывода читают или записывают непрерывную последовательность байтов. При использовании этих функций со структурами необходимо просто передавать функции адреса структур.

546. Вложенные структуры

В структуре могут быть члены любого типа {inl, float и т.д.). Кроме того, члены структуры могут сами быть структурами. Например, в следующем примере объявление структуры включает структуру типа Date, которая обозначает дату приема служащего:

struct Employee {

char name[64]; // Фамилия

int age; // Возраст

char ssan[ll]; // Социальный номер служащего

struct Date {

int day;

int month;

int year;

} hire_date;

int pay_grade; // Разряд по оплате

float salary; // Зарплата

unsigned employee_number; // Номер служащего

} new_employee;

Для обращения к члену вложенной структуры используется операция "точка" (.), сначала указывается член внешней структуры, затем член вложенной структуры:

new_employee.hire_date.month = 12;

547. Структура, содержащая массив

Типы членов структуры могут быть любыми, в том числе структурами или массивами. Если член структуры - массив, то обращаться к нему можно по имени, которому должна предшествовать операция "точка" (.). В следующей программе STRUARRA.C инициализируются поля структуры, включая массив. В программе выполняется цикл для вывода на экран всех элементов массива:

#include <stdio.h>

void main(void)

{

struct Date {

char month_name[64];

int month;

int day;

int year;

} current_date = ( "Июль", 7, 4, 1994 );

int i;

for (i = 0; current_date.month_name[i]; i++)

putchar(current_date.month_name[i]);

}

548. Создание массива структур

Как известно, использование массивов дает возможность хранить множество значений одного и того же типа. Большинство массивов этого раздела имеет тип int, float или char. Однако Си дает возможность объявить массив типа структуры. Например, в следующем объявлении создается массив, в котором хранятся сведения о 100 служащих:

struct Employee {

char name[64]; // Фамилия

int age; // Возраст

char ssan[ll]; // Социальный номер служащего

int pay grade; // Раэряд по оплата

float salary; // Зарплата

unsigned employee_number; // Номер служащего

} staff[100];

Если предположить, что значения для каждого служащего уже проинициализированы, то в следующем фрагменте внутри цикла for выводится имя и номер каждого служащего:

for (emp = 0; emp < 100; emp++)

printf("Номер служащего %s - %d\n",

staff[emp].name, staff[emp].employee_number);

При использовании массива структур операция "точка" (.) применяется ко всем элементам массива.

4.12. Структуры и объединения

В языках Си/Си++ понятие структуры аналогично понятию записи (record) в Паскале. Это структурированный тип данных, представляющий собой поименованную совокупность разнотипных элементов. Тип структура обычно используется при разработке информационных систем, баз данных.

Правила использования структур обсудим на примере, аналогичном тому, который рассматривался в разделе 3.20 применительно к Паскалю.

Сведения о выплате студентам стипендии требуется организовать в виде, показанном на рис. 46.

Элементы такой структуры (фамилия, курс, группа, стипендия) называются полями. Каждому полю должно быть поставлено в соответствие имя и тип.

Формат описания структурного типа следующий:

struct имя_типа

{определения_элементов};

В конце обязательно ставится точка с запятой (это оператор). Для рассмотренного примера определение соответствующего структурного типа может быть следующим:

struct student { char fam[30];

int kurs;

char grup[3];

float stip;

};

После этого student становится именем структурного типа, который может быть назначен некоторым переменным. В соответствие со стандартом Си это нужно делать так:

struct student stud1, stud2;

Правила Си++ разрешают в этом случае служебное слово struct опускать и писать

student stud1, stud2;

Здесь stud1 и stud2 — переменные структурного типа. Допускаются и другие варианты описания структурных переменных. Можно вообще не задавать имя типа, а описывать сразу переменные:

struct (char fam[30];

int kurs;

char grup[3];

float stip;

} studi, stud2, *pst;

В этом примере кроме двух переменных структурного типа объявлен указатель pst на такую структуру. В данном описании можно было сохранить имя структурного типа student.

Обращение к элементам (полям) структурной величины производится с помощью уточненного имени следующего формата:

имя_структуры.имя_элемента

Снова все похоже на Паскаль. Примеры уточненных имен для описанных выше переменных:

studi.fam; stud1.stip

Значения элементов структуры могут определяться вводом, присваиванием, инициализацией. Пример инициализации в описании:

student studl={"Кротов", 3, "Ф32", 350};

Пусть в программе определен указатель на структуру

student *pst, stud1;

Тогда после выполнения оператора присваивания

pst=&studl;

к каждому элементу структурной переменной studi можно обращаться тремя способами. Например, для поля fam

stud1.fam или (*pst).fam или pst→fam

В последнем варианте используется знак операции доступа к элементу структуры: —>. Аналогично можно обращаться и к другим элементам этой переменной:

pst->FIO, pst->grup, pst->stip.

Поля структуры могут сами иметь структурный тип. Такие величины представляют многоуровневые деревья.

Допускается использование массивов структур. Например, сведения о 100 студентах могут храниться в массиве, описанном следующим образом:

student stud[100];

Тогда сведения об отдельных студентах будут обозначаться, например, так: stud[l].fam, stud [5].kurs и т.п. Если нужно взять первую букву фамилии 25-го студента, то следует писать:

stud[25].fam[0].

Пример 1. Ввести сведения об N студентах. Определить фамилии студентов, получающих самую высокую стипендию.

#include <stdio.h>

Элемент структуры типа поля битов. Использование структуры в программе на Си позволяет работать с отдельными битами, т.е. с разрядами двоичного кода. Для этого используются элементы структуры типа поля битов. Формат структуры, содержащий поля битов, следующий:

В качестве типа полей могут использоваться спецификаторы int, unsigned, signed. Минимальной величиной такого типа может быть структура, состоящая всего из одного битового поля. Пример описания такой структуры:

Конечно, для переменной cod в памяти будет выделено 8 бит (1 байт), но использоваться будет только один первый бит.

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

union имя_типа

{определения элементов};

Объединение отличается от структуры способом организации во внутренней памяти. Все элементы объединения в памяти начинаются с одного байта.

Пусть в программе описана структура:

struct S

{ int i;

char ch;

long int L;

};

Расположение ее элементов в памяти будет следующим:

Элементы структуры занимают последовательные ячейки памяти с размером, соответствующим типу. Общий размер структуры равен сумме длин полей.

А теперь рассмотрим объединение со следующим описанием:

union S

{ int i;

char ch;

long int L;

);

Величина с таким типом в памяти будет расположена следующим образом:

Поля объединения накладываются друг на друга. Общий объем занимаемой памяти равен размеру самого большого поля.

Изменение значения любого поля объединения меняет значения других полей.

Пример 2. Составим программу решения следующей задачи: с клавиатуры вводится символ. Вывести на экран двоичный код этого символа.

В этой программе переменная-объединение u содержит два элемента: символьное поле ch и битовую структуру cod, которые накладываются друг на друга. Таким образом, оказывается возможным получить доступ к каждому биту кода символа. Работа программы заканчивается после ввода символа q. Вот вариант результатов работы данной программы:

s: 01110011

d: 01100100

J: 01101010

a: 01100001

b: 01100010

с: 01100011

d: 01100100

q: 01110001

Упражнения

1. Сведения о каждом химическом элементе из периодической таблицы Д. И. Менделеева представить в виде структуры. Написать программу ввода таблицы в память компьютера.

2. Представить координаты точки в трехмерном пространстве в виде структуры, состоящей из трех вещественных полей. Написать программу ввода координат двух точек и вычисления расстояния между ними.

3. Рассматривая комплексное число как структуру, состоящую из двух вещественных полей, составить функции выполнения четырех арифметических операций с комплексными числами.

14. Лекция: Структуры.

-Определение структурных переменных.

-Доступ к компонентам структуры.

-Поля битов в структурах.

-Объединения.

-Перечисления.

­­-Переменные структуры.

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

-Массив структур.

-Переименования типов.

Определение структурных переменных

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

struct имя_структуры {

Описание_элементов

};

Пример:

struct dinner {

char *plase;

float cost;

struct dinner *next;

};

Структурная переменная описывается с помощью переменной структурного типа.

Примеры:

struct dinner week_days [7]; /* массив структур */

struct dinner best_one; /* одна структурная переменная */

struct dinner *p; /* указатель на структурную переменную */

Структура, запись в терминологии языка Паскаль и Ада, - это составной объект, в который входят компоненты любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры указывается записью вида

struct{

список описаний

}

В структуре должен быть указан хотя бы один компонент. Указатель типа структуры используется для определения структур. Определения структур имеют следующий вид:

тип-данных описатели;

где тип-данных указывает тип структуры для объектов, определяемых в описателях. В своей простейшей форме описатели представляют собой обычные имена переменных, массивов, указателей и функций. Например, с помощью определения

struct {

double x,y;

} a,b,c[9];

переменные a и b определяются как структуры, каждая из которых состоит из двух компонентов - x и y. Переменная с определяется как массив из девяти таких структур.

Из определения

struct {

int year;

short int month, day;

} date1,date2;

следует, что каждая из двух переменных date1, date2 состоит из трех компонентов: year, month, day.

С типом структуры может быть ассоциировано имя, которое задается описанием типа в форме

typedef struct {

список описаний

} имя-типа-структуры;

Спецификатор typedef (определяет класс памяти) позволяет нам создать свое собственное имя типа. Это напоминает директиву #define, но со следующими тремя изменениями:

В отличие от #define спецификатор typedef дает символические имена, но ограничивается только типами данных.

Спецификатор typedef выполняется компилятором, а не препроцессором.

В своих пределах спецификатор typedef более гибок, чем #define.

В дальнейшем эти имена могут использоваться для определения структур. Ниже приведен пример описания типа структуры с именем employee:

typedef struct {

char name[30];

int id;

dept d;

family f;

} employee;

где слова dept, family указывают типы, а именно типы структур, предварительно определенные пользователем. Тип структуры employee может быть использован для определения переменных. Например, определение

employee chairperson, president, e1, e2;

описывает переменные chairperson, president, e1, e2 как структуры типа employee.

Существует и другой способ ассоциирования имени с типом структуры. Этот способ основан на применении меток структуры. Метки структуры аналогичны меткам перечисляемого типа. Метка структуры описывается следующим образом:

struct метка{

список описаний

}

где метка является идентификатором. В приведенном ниже примере слово student описывается как метка структуры:

struct student {

char name[25];

int id,age;

char sex;

};

Метки структуры используются для определения структур записью вида

struct метка список-идентификаторов;

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

struct node {

int data;

struct node *next;

};

метка структуры node действительно является рекурсивной, так как она используется в своем собственном описании, т.е. в описании указателя next. Из-за наличия знака * переменная next описана как указатель на объекты типа node. Структуры не могут быть прямо рекурсивными. Структура типа S не может содержать компонент, являющийся структурой типа S. Однако структура типа S может содержать компонент, указывающий на структуру типа S.

Соседние файлы в папке Л№23.Структуры.ООП