Similar presentations:
Пользовательские типы данных. Тема 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(©);
Функции decr() и sign() изменяют значение своих аргументов,
поэтому нужно создать копию параметра r
sign(©);
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