Лекция 6. Пользовательские типы данных ####################################################################################### Простые типы =========================================================== Классификация ----------------------------------------------------------- Стандартные и нестандартные типы `````````````````````````````````````````````````````````` * Использование только стандартных типов в программе уменьшает её возможности. * Стандартные типы не обладают информативностью. * Нестандартные типы данных получаются с помощью специальных механизмов. * стандартные (встроенные) - числовые (**char, short, long, int, ....**) * нестандартные - псевдонимы (**typedef**) - перечисления (**enum**) - структуры (**struct**) - объединения (**union**) - поля битов Описания нестандартных типов `````````````````````````````````````````````````````````` Описания нестандартных типов обычно размещают: * В начале программы (до первого использования). * В заголовочных файлах. Псевдонимы ----------------------------------------------------------- .. admonition:: Определение .. code-block:: c typedef type1 type2; .. code-block:: c typedef unsigned long UL; typedef char * PCHAR; typedef short INT16; .. code-block:: c UL var1, var2, *var3; PCHAR s="Hello!"; INT16 arr[10]; *Использование:* * для сокращения длинных объявлений * для повышения наглядности * для создания переносимых программ Упрощение сложных объявлений `````````````````````````````````````````````````````````` .. admonition:: Задача Объявить массив из N указателей на функции, возвращающих указатели на функции, возвращающие указатель на char. Можно объявить этот массив так: .. code-block:: c char *(*(*a[N])())(); А можно воспользоваться **typedef**: .. code-block:: c typedef char *pc; typedef pc fpc(); typedef fpc *pfpc; typedef pfpc fpfpc(); typedef fpfpc * pfpfpc; pfpfpc a[N]; Перечисления ----------------------------------------------------------- .. admonition:: Определение .. code-block:: c enum NAME {val1,val2,val3, ...}; Примеры: .. code-block:: c enum SPECTRUM { red, orange, yellow, green, blue, violet }; enum LEVELS { low=100, medium=500, high=1000}; enum LEVELS lev1=medium; if(lev1>low) {...} Использование: * для улучшения типизации * для повышения наглядности Основой для внутреннего представления перечислимого типа является тип **int**. Слабая типизация С позволяет смешивать в программе числовые константы и перечислимые типы, а также использовать арифметические операции (**++**). .. code-block:: c enum WEEK {MO,TU,WE,TH,FR,SA,SU}; enum WEEK day; for(day=MO;day**. * Полем структуры может быть указатель на структуру. Доступ к значениям полей `````````````````````````````````````````````````````````` Для доступа к полю через структурный объект используется ''точка'' .. code-block:: c struct BOOK book, *pbook=&book; book.price=123.50; strcpy(book.title,"The C programming"); pbook->pages=300; strcpy(pbook->author,"K&R"); Для доступа к полю через адрес объекта используется ''стрелочка'' .. code-block:: c struct BOOK book, *pbook=&book; (&book)->price=123.50; strcpy((&book)->title,"The C programming"); (*pbook).pages=300; strcpy((*pbook).author,"K&R"); Вложенные структуры `````````````````````````````````````````````````````````` Структуры можно вкладывать друг в друга (''матрёшки'') .. code-block:: c struct TIME { int hh; int mm; int ss; }; struct EVENT { char title[256]; struct TIME when; }; .. code-block:: c 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; Присвоение структур `````````````````````````````````````````````````````````` Допускается присваивать структурные объекты как обычные переменные при соблюдении следующих условий: #. Объекты одного типа #. В объекте нет полей-указателей Если существуют поля-указатели, то после присваивания могут возникнуть двойные ссылки на один и тот же участок памяти. Как же присваивать структуры? Через присвоение значения поля другому полю. При встрече с указателями нужно выделить память под копию данных, затем скопировать данные из одной области памяти в другую. .. code-block:: c 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"); .. code-block:: c 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"); Структуры и функции `````````````````````````````````````````````````````````` Структуры можно передавать в функцию как по **значению**, так и по **указателю**. .. code-block:: c 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); } В отличии от массивов, структуры можно возвращать из функций: .. code-block:: c struct BOOK printBook(struct BOOK book) { printf("Title: %s \nAuthor: %s \nPrice: %.2f\n", book.title,book.author,book.price); return book; } Структуры и указатели `````````````````````````````````````````````````````````` Структура может содержать указатель на себя в качестве поля: .. code-block:: c struct BOOK { char *title; char *author; float price; struct BOOK *next; }; Благодаря полю **next** можно связывать структурные объекты между собой (создавать *связанные списки*). Объединения ----------------------------------------------------------- **Объединения** описываются практически так же, как и структуры, но вместо **struct** используется **union**. .. admonition:: Описание объединения .. code-block:: none union TAG { type1 field1; type2 field2; ... }; В отличие от структур, размер объединения определяется размером его самого длинного поля. Структуры и объединения `````````````````````````````````````````````````````````` Рассмотрим почти одинаковые описания. В чем разница между ними? .. code-block:: c struct A { char data[4]; long value; }; .. code-block:: c union B { char data[4]; long value; }; Объект типа **А** будет занимать в два раза больше памяти, поскольку нужно одновременно хранить значение массива и целой величины. Объект типа **В** будет хранить в **одной** области памяти **либо** массив байт, **либо** целое значение. Размер памяти, выделяемый под объединение равен размеру самого большого поля. Безымянные объединения `````````````````````````````````````````````````````````` **Безымянные объединения** используются для экономии памяти. .. code-block:: c int main() { union { char data[4]; long value; float * ptr; }; value=10; ... ptr=&... ... data[0]='a'; ... }; Применение объединений `````````````````````````````````````````````````````````` Объединения могут использоваться для: * Для экономии памяти (особенно во встроенных системах). * Для исследования значений отдельных байтов многобайтных величин. * Для интерпретации данных, расположенных в некоторой области памяти. Поля битов ----------------------------------------------------------- Поле битов `````````````````````````````````````````````````````````` В С есть возможность организовать память для хранения величин, размером менее 1 байта. Это возможность предоставляется **полями битов**. **Поле битов** -это структура, где для каждого поля указывается ширина в двоичных разрядах. .. admonition:: Определение .. code-block:: none struct TAG { type field1: width1; type field2: width2; ... type fieldN:widthN; }; Поле битов реализуется как набор смежных полей, размещенных внутри типа **signed int** или **unsigned int**. Смещение границ `````````````````````````````````````````````````````````` Что происходит, если общее количество бит, превышает размер *int*? В этом случае используется следующая ячейка памяти типа *int*. Компилятор автоматически смещает определения накладывающихся полей так, чтобы поле прилегала к границам ячейки *int*. Пример `````````````````````````````````````````````````````````` Пример битового поля с максимально компактным заполнением (4 байта) .. code-block:: c 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*. .. image:: _static/06/field1.png Пример битового поля с ''дырами'' .. code-block:: c 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 бита? * Приведите пример битового поля с ''дырами''.