154.34K
Категория: ПрограммированиеПрограммирование

Пользовательские типы данных. Тема 10

1.

Пользовательские
типы данных
Тема 10

2.

Структуры - это
пользовательские типы данных
составные типы данных, построенные с
использованием других типов, встроенных или
пользовательских
Язык Си. Тема 10
2

3.

Объявление структуры
Объявление структуры начинается с ключевого слова
struct. После него идет тег (tag) или имя
структуры. Это называется заголовок структуры
После заголовка идет тело структуры. Тело структуры
заключается в фигурные скобки, после которых
ставится точка с запятой
В теле структуры прописываются поля структуры. Для
каждого поля указывается тип и имя поля,
уникальное в пределах структуры
Если поля имеют один и тот же тип, их можно
перечислить через запятую, указав тип один раз
В качестве поля структуры может выступать
переменная, массив или указатель
Язык Си. Тема 10
3

4.

Пример объявления структуры
struct Time
{
int hour;
int minute;
int second;
};
struct Time
Два варианта
объявления одной
и той же структуры
{
int hour, minute, second;
};
Язык Си. Тема 10
4

5.

Имя структуры
В классическом Си именем нового типа будут два
слова struct + tag
В классическом Си имена структур принято писать
заглавными буквами
Язык Си. Тема 10
5

6.

Объявление экземпляров
структуры
После объявления структуры можно объявлять
переменные типа структуры
Переменные типа структуры называются экземпляры
структуры
Можно создать экземпляры структуры, массивы
экземпляров структуры, указатели на структуру
Экземпляр
struct Time timeObject;
Массив
struct Time timeArray[10];
Указатель
struct Time *timePtr;
Язык Си. Тема 10
6

7.

Объявление экземпляров
структуры
Если структура объявлена со словом typedef, то слово
struct в объявлении экземпляра структуры не нужно
использовать
typedef struct
{
int hour, minute, second;
}Time;
Time t = { 3, 4, 5 };
Язык Си. Тема 10
7

8.

Создание единственного
экземпляра структуры
Если структура не имеет тега, то можно создать
единственный экземпляр такой структуры в момент ее
объявления. Это аналог синглтона в языке Си.
Если такая структура объявлена вне функций, то все ее
поля по умолчанию равны 0.
Если внутри функции, то перед использованием поля
структуры необходимо проинициализировать
struct
{
int hour, minute, second;
}Time;
Time.minute = 4;
Язык Си. Тема 10
8

9.

Основы работы со структурой
При объявлении структуры память не выделяется
Память выделяется только при создании экземпляров
структуры
Все поля экземпляра структуры располагаются в
памяти последовательно
Инициализация полей структуры возможна в момент
объявления экземпляра структуры
Time t1 = { 3, 4, 5 };
Time t2 = { 3 };
Язык Си. Тема 10
3
3
4
0
5
0
t1
t2
9

10.

Размер экземпляра
структуры
Размер экземпляра структуры выравнивается по
размеру ее самого большого поля (кратен самому
большому полю)
printf("%d\n", sizeof(Time) );
Язык Си. Тема 10
//12
10

11.

Размер экземпляра
структуры
Размер структуры зависит от порядка следования
полей структуры
struct Time
{
short hour;
int minute;
long long second;
};
struct Time
{
short hour;
long long second;
int minute;
};
struct Time tt;
printf("%d\n", sizeof(tt) );
struct Time tt;
printf("%d\n", sizeof(tt) );
16
Язык Си. Тема 10
24
11

12.

Размер экземпляра
структуры
Для внешней структуры выравнивание определяется
по размеру самого большого поля (и внутренней, и
внешней структуры)
struct Foo {
short iiii;
char c;
};
struct Test1 {
char c;
Foo foo;
};
struct Bar {
char c8[4];
};
struct Test2 {
char c;
Bar bar;
};
printf("%d\n", sizeof(Test1) );
//6
printf("%d\n", sizeof(Test2) );
//5
Язык Си. Тема 10
12

13.

Размер экземпляра
структуры
Для внешней структуры выравнивание определяется
по размеру самого большого поля (и внутренней, и
внешней структуры)
struct Foo {
short iiii;
char c;
};
struct Test1 {
double c;
Foo foo;
};
struct Bar {
char c8[4];
};
struct Test2 {
char c;
Bar bar;
};
printf("%d\n", sizeof(Test1) );
//16
printf("%d\n", sizeof(Test2) );
//5
Язык Си. Тема 10
13

14.

Доступ к полям структуры
Для доступа к полям структуры используются
операции точка (.) и стрелка (->)
Операция точка используется для доступа через
экземпляр и через ссылку
Операция стрелка используется для доступа через
указатель
Time timeObject, *timePtr;
timePtr = & timeObject;
printf("%d:%d:%d\n", timePtr->hour,
timePtr->minute,
timePtr->second );
printf("%d:%d:%d\n", timeObject.hour,
timeObject.minute,
timeObject.second );
Язык Си. Тема 10
14

15.

Доступ к полям структуры
Операции точка и стрелка взаимозаменяемы
Для использования стрелки с экземплярами и
ссылками нужно предварительно взять адрес
объекта
Для использования точки с указателями нужно
предварительно разыменовать указатель
Time timeObject, *timePtr;
timePtr = & timeObject;
printf("%d:%d:%d\n", (*timePtr).hour,
(*timePtr).minute,
(*timePtr).second );
printf("%d:%d:%d\n", (&timeObject)->hour,
(&timeObject)->minute,
(&timeObject)->second );
Язык Си. Тема 10
15

16.

Вложенные структуры
Структура может быть объявлена внутри другой
структуры
Для работы с экземплярами внутренней структуры также
используются операции доступа точка и стрелка
struct A
{
struct B
{
int b1;
double b2;
};
struct B b;
double a1;
float a2;
};
struct A
{
struct B
{
int b1;
double b2;
}b;
double a1;
float a2;
};
Два разных варианта объявления
экземпляра внутренней структуры
Язык Си. Тема 10
16

17.

Функции и структуры
Структуры в функцию можно передавать
по значению
по ссылке
по указателю
По умолчанию структуры передаются вызовом по
значению, такой способ приводит к большим
накладным расходам на вызов функции
Предпочтительнее использовать вызов по ссылке или
по указателю
Если функция не должна изменять поля
передаваемой структуры, то нужно передать
константную ссылку или константный указатель
Если функция возвращает экземпляр структуры,
создается его копия
Язык Си. Тема 10
17

18.

Пример - структура
Рациональная дробь
#ifndef RATIONAL_H
#define RATIONAL_H
typedef struct
rat
{
int num, denum;
Заголовочный файл
rational.h
Объявление структуры
Rational и прототипы
функций для работы с этой
структурой.
}Rational;
(const Rational *, const Rational *); Структуры
передаются как
Rational sub (const Rational *, const Rational *); константные
Rational mult (const Rational *, const Rational *); указатели, то есть
функции не
Rational div (const Rational *, const Rational *); изменяют поля
структур
void
print(const Rational *);
Rational add
#endif
Язык Си. Тема 10
18

19.

Внутренние функции модуля
Rational
#include "rational.h"
int NOD(int a, int b)
{
if(! ( a % b ))
return b;
return NOD(b, a % b);
}
Файл исходного кода
rational.cpp
Рекурсивная функция
нахождения наибольшего
общего делителя (для
сокращения дроби)
Функция сокращения дроби
void decr(Rational * r)
{
int a;
a = NOD(r->num, r->denum);
r->denum /= a;
r->num
/= a;
}
Язык Си. Тема 10
19

20.

Внутренние функции модуля
Rational
Файл исходного кода
rational.cpp
Функция определения знака
void sign(Rational * r)
дроби (для печати на экране)
{
if(r->denum < 0 && r->num > 0)
{
r->denum *= -1;
r->num
*= -1;
}
}
Язык Си. Тема 10
20

21.

Функция сложения
рациональных дробей
Файл исходного кода
rational.cpp
Rational add(const Rational *left,
const Rational *right)
{
Rational res;
res.num = left->num
* right->denum +
left->denum
* right->num;
res.denum = left->denum * right->denum;
decr(&res);
Функция сложения двух
sign(&res);
дробей.
return res;
После сложения вызываются
}
функции сокращения дроби и
определения знака дроби.
Функция создает новый
Другие арифметические
экземпляр структуры и
функции выглядят аналогично
возвращает его копию, а не
ссылку на него
Язык Си. Тема 10
21

22.

Функция печати
рациональной дроби
Файл исходного кода
void print(const Rational * r)
rational.cpp
{
Rational copy = *r;
decr(&copy);
Функции decr() и sign() изменяют значение своих аргументов,
поэтому нужно создать копию параметра r
sign(&copy);
if(copy.num / copy.denum)
Если дробь неправильная, печатаем ее целую часть и уменьшаем знаменатель
{
printf("%d ", (copy.num / copy.denum) );
copy.num %= copy.denum;
}
if(copy.num % copy.denum==0)
{
Если числитель кратен знаменателю – выходим из функции
printf("\n");
return;
}
printf("%d/%d\n", copy.num, copy.denum);
}
Язык Си. Тема 10
22

23.

Работа с рациональной дробью
#include "rational.h"
int main()
{
Rational r1, r2, r3;
r1.num = 45;
r1.denum = 15;
print(&r1);
r2.num = 2;
r2.denum = 3;
r3 = add(&r1, &r2);
print(&r3);
r3 = sub(&r1, &r2);
r3 = mult(&r1, &r2);
r3 = div(&r1, &r2);
r3 = r2;
return 0;
}
Язык Си. Тема 10
Файл исходного кода test.cpp
Функция main содержит код,
демонстрирующий работу со
структурой Rational и
функциями, объявленными в
заголовочном файле
rational.h
Одному экземпляру структуры
можно присвоить другой
экземпляр этой же структуры
23

24.

Пример - структура
«Цветной мяч»
typedef struct CB
{
В качестве одного из полей
структура содержит указатель
на тип char
char* color;
double radius;
} ColorBall;
Язык Си. Тема 10
24

25.

Функции для работы с
мячом
void init(ColorBall* pcb, const char* s, double r)
{
pcb->color = (char*) malloc( (strlen(s) + 1) *
sizeof(char) );
strcpy(pcb->color, s);
pcb->radius = r;
Структура содержит указатель,
}
поэтому необходимы функции
для выделения памяти при
инициализации структуры и
void destroy(ColorBall* pcb)
освобождения памяти при
{
уничтожении экземпляра
if(pcb->color)
структуры
free(pcb->color);
}
Язык Си. Тема 10
25

26.

Функции для работы с
мячом
void setColor(ColorBall* pcb, const char* s)
{
if(pcb->color)
free(pcb->color);
pcb->color = (char*) malloc( (strlen(s) + 1) *
sizeof(char) );
strcpy(pcb->color, s);
}
Функции со словом set традиционно
изменяют значения полей.
Изменение цвета требует бОльших усилий,
чем изменение радиуса
void setRadius(ColorBall* pcb, double r)
{
pcb->radius = r;
}
Язык Си. Тема 10
26

27.

Функции для работы с
мячом
char* getColor(const ColorBall* pcb)
{
return pcb->color;
}
double getRadius(const ColorBall* pcb)
{
return pcb->radius;
}
Язык Си. Тема 10
Функции со словом
get традиционно
возвращают
значения полей.
Структура
передается как
указатель на
константу, потому
что поля не
изменяются.
Тип функции
соответствует типу
возвращаемого ею
поля.
27

28.

Функции для работы с
мячом
void print(const ColorBall* pcb)
{
puts("Color Ball:");
puts(pcb->color);
printf("radius: %.2lf\n", pcb->radius);
}
Функция print необходима для вывода на экран
полей экземпляра структуры ColorBall
Структуру сразу целиком вывести на экран
невозможно, только поэлементно
Язык Си. Тема 10
28

29.

Работа со структурой
int main()
{
ColorBall cb1;
init(&cb1, "red", 40);
print(&cb1);
ColorBall cb2;
Присваивание структур, содержащих указатели,
cb2 = cb1;
может привести к краху программы
print(&cb2);
setColor(&cb1, "violet");
setRadius(&cb1, 24.2);
print(&cb1);
print(&cb2);
destroy(&cb1);
destroy(&cb2);
return 0;
}
Язык Си. Тема 10
29

30.

«Глубокое" копирование
int main()
{
ColorBall cb1;
init(&cb1, "red", 40);
print(&cb1);
ColorBall cb2;
cb2.color = (char*) malloc( (strlen(cb1.color) + 1) *
sizeof(char) );
strcpy(cb2.color, cb1.color);
cb2.radius = cb1.radius;
Присваивание структур, содержащих
print(&cb2);
setColor(&cb1, "violet");
указатели, должно быть «глубоким».
setRadius(&cb1, 24.2);
Оно должно включать:
print(&cb1);
освобождение ранее
print(&cb2);
выделенной памяти,
destroy(&cb1);
выделении новой памяти
destroy(&cb2);
поэлементного заполнения
return 0;
новой памяти
}
Язык Си. Тема 10
30

31.

Структуры с
самоадресацией
Структура в качестве поля может содержать адрес
самой себя. Такие структуры называются
структурами с самоадресацией
Структуры с самоадресацией позволяют
организовывать связные списки, деревья, графы и
т.д.
struct Node
{
int value;
struct Node* pnext;
};
Язык Си. Тема 10
31

32.

Поля битов
Для поля структуры можно указать точное количество
бит, которое оно будет занимать в памяти. Такие
поля называются полями бит
Полями бит могут быть только целочисленные типы
Поля бит упаковываются в машинное слово
Поля бит не могут быть организованы в массив
От поля бит нельзя взять адрес
Язык Си. Тема 10
32

33.

Поля битов
struct TimeAndDate
{
unsigned hours
:5; // часы от 0 до 24
unsigned mins
:6; // минуты от 0 до 60
unsigned secs
:6; // секунды от 0 до 60
unsigned weekDay
:3; // день недели
unsigned monthDay :6; // день месяца от 1 до 31
unsigned month
:5; // месяц от 1 до 12
unsigned year
:8; // год от 0 до 100
};
Язык Си. Тема 10
33

34.

Объединения
Объявляются так же как структуры, вместо struct
используется слово union
Все поля объединения располагаются по одному
адресу
Изменение одного поля объединения может привести
к изменению значений других полей
Размер объединения определяется самым большим
полем объединения
Используются для экономии памяти и в
исследовательских целях
Язык Си. Тема 10
34

35.

Объединения
struct Value {
enum NumberType { ShortType, LongType, DoubleType };
enum NumberType type;
short sx;
// если type равен ShortType
long lx;
// если type равен LongType
double dx;
// если type равен DoubleType
};
struct Value {
enum NumberType { ShortType, LongType, DoubleType };
enum NumberType type;
union Number
{
short sx;
// если type равен ShortType
long lx;
// если type равен LongType
double dx;
// если type равен DoubleType
}val;
};
Язык Си. Тема 10
35

36.

Объединения
Язык Си. Тема 10
36

37.

Объединения
union View
{
float b;
int a;
};
Объединение для изучения
побитового представления
числа с плавающей точкой
int main()
{
union View v;
puts("Enter a number: ");
scanf("%f" , &v.b);
char str[33];
itoa(v.a, str, 2);
puts(str);
printf("%d\n", v.a);
printf("%f\n", v.b);
return 0;
}
Язык Си. Тема 10
37

38.

Безымянные объединения
Позволяют экономить память
struct Test
{
union
{
char c[8];
double b;
int* a;
};
};
int main()
{
int d = 5;
struct Test t;
t.a = &d;
printf("%lf\n", t.b);
printf("%x\n", t.a);
printf("%c\n", t.c[2]);
}
Язык Си. Тема 10
38

39.

Конец
Язык Си. Тема 10
39
English     Русский Правила