Слайд 2Структура, также как и массив, не является типом данных – это
лишь способ адресации данных в памяти, также, как и массивы. Структура может содержать в себе любые типы данных, а также структуры и массивы.
Тип данных структуры – указатель на область памяти, которую покрывает структура. Но это особый указатель, и работа с ним производится по-другим правилам, чем если бы мы работали просто с указателем. Эти правила мы рассмотрим далее в этом разделе.
Слайд 3Дадим определение структуры
struct
{
// элементы структуры – любые переменные
// и любые
типы данных
};
Например:
struct TPerson
{
char name[10], surname[20];
unsigned long telephone_nr;
char address[30];
};
struct Circle{ int x, y; double radius; };
Слайд 4Давайте подсчитаем размер структур
Tperson ( по логике, согласно элементам структуры, как
мы ее определили ): 10 * sizeof( char ) + 20 * sizeof( char ) + sizeof( unsigned long ) + 30 * sizeof( char ) = 10*1+20*1+8+30*1 = 68
Но если запросить размер структуры, то заметим интересную вещь – размеры не совпадают.
sizeof( struct TPerson ) = 72!
Здесь нет никакого подвоха. Действительно, данные, которые мы определили в структуре, занимают 68 байт в памяти, но вся структура занимает 72 байта в памяти.
Вывод: структура – это не только данные, которые она адресует! В структуре пристутствует еще один невидимый фактор, который рассмотрим в следующем слайде.
Слайд 5Выравнивание ( alignment ) в памяти
В памяти, и, соответственно, в структурах
также, данные расположены не сплошь непрерывным потоком байт. Для ускорения доступа к памяти и операций чтения / записи, реализован механизм выравнивания.
Данные выравниваются по границе в 4 байта ( зависит от архитектуры ), проще говоря, по границе int. В архитектурах х86 и х86_64 sizeof(int) = 4.
К примеру, мы создали структуру struct t{ char a; int b };
сhar занимает 1 байт в памяти, int – 4 байта.
В памяти эта структура представлена таким образом
байт под букву ( а ), 3 пустых байта ( выравнивание ), 4 байта под число ( b ). В сумме, структура займет 8 байт в памяти, а не 5, как может показаться. Данные займут 5 байт, но вся структура – 8, из-за эффекта выравнивания.
Слайд 6Вернемся к нашим структурам...
Tperson: 10 байт выделено в памяти под имя,
тоесть базовый адрес + 10. Далее записывается фамилия, данные должны быть записаны по 4-х байтной границе, тоесть ближайший адрес – базовый адрес + 12. образуются 2 пустых байта между именем и фамилией. Фамилия заканчивается по смещению 32, что есть 4-х байтная граница, и телефон пишется сразу следом за фамилией, без промежутков. Телефон в памяти заканчивается по смещению 40, от базового адреса, поскольку тип unsigned long занимает 8 байт в памяти ( на архитектурах x86(_64) ). Смещение +40 есть 4-х байтная граница, поле “адрес” пишется сразу следом, “адрес” заканчивается по смещению 70, но опять же, это не 4-х байтная граница, и в структуру в памяти добавляются 2 лишних байта в конец. Таким образом, мы получаем
Sizeof( struct TPerson ) = 72, хотя под сами данные выделено 68 байт.
В структуре Circle никаких промежутков между полями данных нет, поскольку мы использовали типы данных int и float, они занимают по 4 и 8 байт в памяти соответственно, все смещения соответствует границе int, потому sizeof( struct Circle ) = 2 * 4 + 8 = 16 байт.
Слайд 7Статическое и динамическое выделение памяти под структуры.
Статическое выделение памяти – структура
создается в стэке.
// определили структуру
struct TPerson{ … };
// создали 2 структуры employee и manager
struct TPerson employee, manager;
Указатель на структуру ( объявление ):
struct TPerson* employee_ptr;
employee_ptr = &employee; // теперь указатель employee_ptr указывает на структуру employee.
или одной строкой:
struct TPerson* manager_ptr = &manager; // теперь указатель manager_ptr указывает на структуру manager.
Слайд 8Другой способ статического выделения памяти – определение и создание структуры одним
выражением.
struct TPerson
{
...
} employee, manager, *employee_ptr = &employee,
*manager_ptr = manager;
Здесь мы определили структуру TPerson, и создали две структуры типа TPerson – employee и manager, а также объявили два указателя на структуру типа TPerson – employee_ptr и manager_ptr и присвоили им соответствующие значения, тоесть выставили указатель employee_ptr на структуру employee, а указатель manager_ptr указывает на структуру manager.
Эти указатели имеют тип “указатель на структуру TPerson” - struct TPerson*.
Слайд 9Динамическое выделение памяти – структура создается в области памяти, называемой куча
( heap ).
struct TPerson* employee = ( struct TPerson* ) malloc( sizeof( struct TPerson ) );
if( ! Employee )
{
fprintf( stderr, “Cannot allocate memory for structure.\n” );
return (-1);
}
...
// чтение / запись в структуру
free( employee ); // возвращаем выделенную память системе
Когда мы выделили память под структуру динамически, мы получаем указатель на эту область памяти. Работа со структурами в этом случае происходит через указатель. Об этом далее.
Слайд 10Работа со структурами ( чтение, запись )
В случае статически выделенной структуры
обращение к элементу структуры происходит следующим образом:
имя_переменной.элемент структуры
Примеры:
struct Tperson{ … } employee, manager;
struct Circle; Circle circle; // создали эземпляр структуры
employee.telephone_nr = 12345;
manager.telephone_nr = 54321;
printf( “employee nr: %lu manager nr: %lu\n”,
employee.telephone_nr, manager.telephone_nr );
circle.x = 10; circle.y = 20;
while( counter ){ circle.x++; circle.y--; }
Слайд 11Обращение к структуре по указателю.
Разыменование указателя.
Если мы выделили память под
структуру динамичеси, то работа с элементами структуры происходит через указатели. Это справедливо и для статически выделенных структур, если создать указатель на такую структуру, то со структурой можно точно так же работать через этот указатель. Обращение к элементу структуры по указателю называется разыменованием указателя ( pointer dereference ).
Слайд 12Struct Circle
{
int x, y;
double radius;
} circle, *circle_ptr=
&circle;
struct Circle* some_pointer;
some_pointer = &circle;
circle.x = 5;
(*circle_ptr).y = 10;
circle_ptr->radius = 2;
some_pointer->radius++;
Оператор → соответствует значению по указателю.
Т.о circle_ptr->radius то же самое, что (*circle_ptr).radius
Printf( “Circle x: %d\nCircle y: %d\nCircle radius: %f\n”,
circle.x, circle_ptr->y, circle_ptr->radius );
Слайд 13В этом слайде будем делать из структуры тип данных
Для этого в
Си существует ключевое слово typedef ( type define, определить свой тип данных ).
Применение простое
typedef struct TCircle { int x,y; double radius } Circle, *PCircle;
Мы только что создали свой тип данных Circle, и указатель на тип Circle c именем Pcircle. Если указатель не объявлять отдельно в typedef конструкции, то указатель будет выглядеть как обычный указатель на тип – Circle* name. В нашем же случае, мы объявили два типа данных: создали свой тип данных Circle, и еще один тип данных – указатель на Circle cо своим именем PCircle;
Circle my_circle; // объявили переменную типа Circle с именем my_circle.
Без typedef конструкции эта строка выглядела бы так:
struct TCircle my_circle;
Слайд 14 typedef struct TCircle{ int x,y; double radius;
} Circle, *PCircle;
Circle my_circle;
PCircle my_circle_ptr = &my_circle;
my_circle.x = 5;
(*my_circle_ptr).y = 10;
my_circle_ptr->radius = 2;
Circle* pointer;
pointer = &my_circle;
pointer->radius += 8;
printf( "Circle x: %d\n", my_circle.x ); // 5
printf( "Circle y: %d\n", pointer->y ); // 10
printf( "Circle radius: %.2f\n", my_circle_ptr->radius ); // 10
Слайд 15Создать таблицу базы данных, в которой присутствют поля: имя, фамилия, личный
код ( Estonian ID code ), студенческий код.
Записать в файл данные по 10 людям и считать их из файла.
Произвести сортировку по созданной базе данных по личному коду ( м / ж )
Студентов и студенток в свою очередь отсортировать по возрасту, студентов в возрастающем порядке, студенток в убывающем порядке.
Задание #1
Слайд 16Задание #2
Создать таблицу БД как минимум с 5 машинами. Поля: марка,
модель, номерной знак, год выпуска, объем двигателя.
Также создать таблицу БД владельцев машин. Поля: личный код, имя.
Найти самую мощную, самую старую и самую новую машину в базе.
У одного владельца может быть несколько машин, но машина может принадлежать только одному владельцу.
Слайд 17Задание #3
Создать 2 таблицы БД. В одной данные студентов ( имя,
фамилия, студенческий код ), в другой соответствующие данные по предметам ( название предмета, код предмета, студенческие коды студентов, учащих этот предмет, результат ). Программа создает в результате экзаменационные листы по разным предметам в отдельных файлах, в которых:
Название предмета в имени файла или в заголовке
Имя и фамилия студента
Его студенческий код
Результат экзамена
Сортировка студентов осуществляется по фамилиям.
Слайд 18Задание #4
Создать таблицу БД дат ( число, месяц, год ) и
ввести в нее данные из файла
Отсортировать данные по годам в отдельные массивы, и каждый массив отсортировать в возрастающем порядке по признаку ( месяц, число )
Слайд 19Задание #5
Создать таблицу БД певческого хора со следующими полями: имя. Фамилия,
личный код, тембр голоса
Внести данные по 10 людям, либо вручную, либо из текстового файла, который сделаете сами.
Произвести поиск по тембру голоса на 4 категории ( сопрано, альт, тенор и бас )
В каждой из групп отсортировать людей по возрасту: женщин в возрастающем порядке, мужчин в убывающем порядке.
Слайд 20Задание на сортировку строк
Создать текстовый файл, с 4-х значными последовательностями букв
и цифр ( 23IO, 9010, … )
Считать файл программно и создать на его основе массив строк.
Задача состоит в определении, является ли запись:
а) bin-числом
б) hex-числом
вне зависимости от регистра символов.
Результат вывести в файл в виде записей ( строка, база – 2 или 16 ) в возрастающем порядке найденные числа. В самом конце вывести строки, которые не принадлежат ни одной из вышеперечисленных категорий.
В программе для представления каждой записи использовать структуры.