Лекция 6. Пользовательские типы данных¶
Простые типы¶
Классификация¶
Стандартные и нестандартные типы¶
Использование только стандартных типов в программе уменьшает её возможности.
Стандартные типы не обладают информативностью.
Нестандартные типы данных получаются с помощью специальных механизмов.
стандартные (встроенные) - числовые (char, short, long, int, ....)
нестандартные
- псевдонимы (typedef)
- перечисления (enum)
- структуры (struct)
- объединения (union)
- поля битов
Описания нестандартных типов¶
Описания нестандартных типов обычно размещают:
- В начале программы (до первого использования).
- В заголовочных файлах.
Псевдонимы¶
typedef type1 type2;
typedef unsigned long UL;
typedef char * PCHAR;
typedef short INT16;
UL var1, var2, *var3;
PCHAR s="Hello!";
INT16 arr[10];
Использование:
- для сокращения длинных объявлений
- для повышения наглядности
- для создания переносимых программ
Упрощение сложных объявлений¶
Задача
Объявить массив из N указателей на функции, возвращающих указатели на функции, возвращающие указатель на char.
Можно объявить этот массив так:
char *(*(*a[N])())();
А можно воспользоваться typedef:
typedef char *pc;
typedef pc fpc();
typedef fpc *pfpc;
typedef pfpc fpfpc();
typedef fpfpc * pfpfpc;
pfpfpc a[N];
Перечисления¶
enum NAME {val1,val2,val3, ...};
Примеры:
enum SPECTRUM { red, orange, yellow, green, blue, violet };
enum LEVELS { low=100, medium=500, high=1000};
enum LEVELS lev1=medium;
if(lev1>low) {...}
Использование:
- для улучшения типизации
- для повышения наглядности
Основой для внутреннего представления перечислимого типа является тип int.
Слабая типизация С позволяет смешивать в программе числовые константы и перечислимые типы, а также использовать арифметические операции (++).
enum WEEK {MO,TU,WE,TH,FR,SA,SU};
enum WEEK day;
for(day=MO;day<SA;day++)
// обработка рабочих дней
В С++ (с сильной типизацией) данный пример вызовет ошибку и будет требовать перегрузки арифметического оператора ++ для перечислимого типа.
Как связать перечисление со строкой¶
enum SPECTRUM { red, blue };
// первый способ
char *colors[]={"RED","BLUE"};
int main()
{
enum SPECTRUM color=blue;
// второй способ
switch(color)
{
case red:
printf("RED\n");
break;
case blue:
printf("BLUE\n");
break;
}
printf("%s",colors[red]);
return 0;
}
Составные типы¶
Структуры¶
Записи¶
Структура (запись) представляет собой набор данных, хранящихся в памяти в смежных адресах, но не обязательно принадлежащих одному типу. Это позволяет рассматривать саму структуру как универсальный тип для представления внутреннего устройства множества объектов.
Изображение массива:

Изображение структуры (записи):

Фундаментальные структуры данных¶
Замечательно то, что массивы и записи можно объединять, создавая:
- Массивы записей.
- Записи, содержащие массивы.
На основе массивов и записей строят:
- Связанный список - набор элементов одного типа, не обязательно следующих в памяти друг за другом и связанных между собой благодаря хранению адресов.
- Графы - множество вершин (узлов), соединённых рёбрами.
- Деревья - иерархически связанные элементы данных, частный случай графа.
- Хэш-таблицы (ассоциативные массивы) - наборы ‘’ключ-значение’‘.
Определение структуры¶
Сначала мы описываем новый структурный тип:
struct TAG
{
type1 field1;
type2 field2;
...
};
Потом создаём объекты:
struct TAG obj1, obj2,.. *pobj,arr[N];
struct BOOK
{
char title[40];
char author[30];
int pages;
float price;
};
struct BOOK mybook;
typedef struct
{
char title[40];
char author[30];
int pages;
float price;
} BOOK;
BOOK mybook;
Инициализация¶
Вариант 1.
struct BOOK b1={"The C programming",
"K & R",
300,
100.23 };
Вариант 2.
struct BOOK
{
char title[40];
char author[30];
int pages;
float price;
}
b1 = {"The C programming",
"K & R",
300,
100.23 };
Свойства структур¶
- В памяти все поля структуры располагаются последовательно.
- Память под структуру выделяется при объявлении переменных.
- Доступ к полям производится с помощью операций . и ->.
- Полем структуры может быть указатель на структуру.
Доступ к значениям полей¶
Для доступа к полю через структурный объект используется ‘’точка’‘
struct BOOK book, *pbook=&book;
book.price=123.50;
strcpy(book.title,"The C programming");
pbook->pages=300;
strcpy(pbook->author,"K&R");
Для доступа к полю через адрес объекта используется ‘’стрелочка’‘
struct BOOK book, *pbook=&book;
(&book)->price=123.50;
strcpy((&book)->title,"The C programming");
(*pbook).pages=300;
strcpy((*pbook).author,"K&R");
Вложенные структуры¶
Структуры можно вкладывать друг в друга (‘’матрёшки’‘)
struct TIME
{
int hh;
int mm;
int ss;
};
struct EVENT
{
char title[256];
struct TIME when;
};
struct EVENT event, *pevent=&event;
strcpy(event.title,"begin");
event.when.hh=18;
event.when.mm=0;
event.when.ss=0;
strcpy(pevent->title,"begin");
pevent->when.hh=18;
pevent->when.mm=0;
pevent->when.ss=0;
Присвоение структур¶
Допускается присваивать структурные объекты как обычные переменные при соблюдении следующих условий:
- Объекты одного типа
- В объекте нет полей-указателей
Если существуют поля-указатели, то после присваивания могут возникнуть двойные ссылки на один и тот же участок памяти.
Как же присваивать структуры? Через присвоение значения поля другому полю. При встрече с указателями нужно выделить память под копию данных, затем скопировать данные из одной области памяти в другую.
struct BOOK
{
char title[40];
char author[40];
float price;
};
struct BOOK book1={"The C Programming",
"K&R", 300.0};
struct BOOK book2;
book2=book1; // поэлементное копирование
// мы меняем только book1, но не book2!
strcpy(book1.title,"The C++ Programming");
struct BOOK
{
char *title;
char *author;
float price;
};
struct BOOK book1={"The C Programming",
"K&R", 300.0};
struct BOOK book2;
book2=book1; // копируем ссылку!
// мы одновременно пытаемся поменять и book1 и book2
// а также "залезаем" в статическую память, вызывая сбой
strcpy(book1.title,"The C++ Programming");
Структуры и функции¶
Структуры можно передавать в функцию как по значению, так и по указателю.
void printBook(struct BOOK book)
{
printf("Title: %s \nAuthor: %s \nPrice: %.2f\n",
book.title,book.author,book.price);
}
void enterBook(struct BOOK *book)
{
fgets(book->title,40,stdin);
fgets(book->author,40,stdin);
scanf("%f",&book->price);
}
В отличии от массивов, структуры можно возвращать из функций:
struct BOOK printBook(struct BOOK book)
{
printf("Title: %s \nAuthor: %s \nPrice: %.2f\n",
book.title,book.author,book.price);
return book;
}
Структуры и указатели¶
Структура может содержать указатель на себя в качестве поля:
struct BOOK
{
char *title;
char *author;
float price;
struct BOOK *next;
};
Благодаря полю next можно связывать структурные объекты между собой (создавать связанные списки).
Объединения¶
Объединения описываются практически так же, как и структуры, но вместо struct используется union.
union TAG
{
type1 field1;
type2 field2;
...
};
В отличие от структур, размер объединения определяется размером его самого длинного поля.
Структуры и объединения¶
Рассмотрим почти одинаковые описания. В чем разница между ними?
struct A
{
char data[4];
long value;
};
union B
{
char data[4];
long value;
};
Объект типа А будет занимать в два раза больше памяти, поскольку нужно одновременно хранить значение массива и целой величины.
Объект типа В будет хранить в одной области памяти либо массив байт, либо целое значение.
Размер памяти, выделяемый под объединение равен размеру самого большого поля.
Безымянные объединения¶
Безымянные объединения используются для экономии памяти.
int main()
{
union
{
char data[4];
long value;
float * ptr;
};
value=10;
...
ptr=&...
...
data[0]='a';
...
};
Применение объединений¶
Объединения могут использоваться для:
- Для экономии памяти (особенно во встроенных системах).
- Для исследования значений отдельных байтов многобайтных величин.
- Для интерпретации данных, расположенных в некоторой области памяти.
Поля битов¶
Поле битов¶
В С есть возможность организовать память для хранения величин, размером менее 1 байта. Это возможность предоставляется полями битов.
Поле битов -это структура, где для каждого поля указывается ширина в двоичных разрядах.
struct TAG
{
type field1: width1;
type field2: width2;
...
type fieldN:widthN;
};
Поле битов реализуется как набор смежных полей, размещенных внутри типа signed int или unsigned int.
Смещение границ¶
Что происходит, если общее количество бит, превышает размер int?
В этом случае используется следующая ячейка памяти типа int.
Компилятор автоматически смещает определения накладывающихся полей так, чтобы поле прилегала к границам ячейки int.
Пример¶
Пример битового поля с максимально компактным заполнением (4 байта)
struct WORD
{
unsigned int a: 2;
unsigned int b: 2;
unsigned int c: 2;
unsigned int d: 2;
unsigned int e: 8;
unsigned int f: 16;
};
В данном случае задействуется одна переменная int.
Пример битового поля с ‘’дырами’‘
struct WORD
{
unsigned int a: 2;
unsigned int b: 3;
unsigned int : 2; // пустой промежуток
unsigned int d: 5;
unsigned int : 4; // пустой промежуток
unsigned int f: 16;
};
Вопросы для самоконтроля¶
- Как классифицируются стандартные и нестандартные типы в С?
- Что такое псевдонимы (typedef)?
- Для чего используются псевдонимы?
- Как с помощью typedef упростить сложное объявление?
- Как задать перечисление?
- Для чего используются перечисления?
- Как связать перечисление со строкой?
- Что такое структура (запись)?
- Чем запись отличается от массива?
- Что строят на основе фундаментальных способов организации данных?
- Как определить структуру?
- Как проинициализировать структуру?
- Перечислите свойства структурного типа.
- Чем структуры отличаются от массивов?
- Как в структуре осуществляется доступ к значениям полей?
- Как создать вложенные структуры?
- Можно ли присваивать структуры?
- Как передать структуру в функцию?
- Можно ли возвратить структуру из функции?
- Могут ли указатели выступать в роли полей структур?
- Может ли структура содержать указатель на себя саму?
- Как задать объединение?
- Чем объединение отличается от структуры?
- Чему равен размер памяти, занимаемый структурой?
- Чему равен размер памяти, занимаемый объединением?
- Для чего используются безымянные объединения?
- Для чего используются объединения?
- Что такое поле битов?
- Что происходит, если суммарный размер полей превышает 32 бита?
- Приведите пример битового поля с ‘’дырами’‘.