Версия:
Аннотация
Построение нового, собственного типа переменных.
Цель. Построить новый тип из уже имеющихся.
Предварительный вариант!
В первом подразделе будет показано как создавать новый тип данных, являющийся составленым из ранее уже имеющихся типов данных.
Допустим в программе нужно обрабатывать сложные объекты, например, точки. Под термином – сложный – понимается, что объект состоит из связанных между собой базовых типов языка Си. Например, точка будет состоять из её компонент, двумерная точка состоит из двух чисел с плавающей точкой.
Важным моментом связанным с объектами является его целостность. Так при выполнении вычислений с такими объектами использую только то, что было ранее введено (т.е. без понятия структур), возникнут ряд сложностей описанных ниже.
Объявление При объявлении сложность заключается в том, что необходимо объявить переменные так, чтобы потом было ясно какие из переменных образует единый объект. В частности, какие из переменных являются компонентами точки и какой у них порядок.
Так, если нужно объявить одну точку, то можно написать код и так:
Но, если точек больше, например две, то придется например вводить индекс:
В дальнейшем в программе необходимо помнить о том, что переменные с одним и тем же индексом (суффиксом) образуют единый объект, в рассматриваемом случае: первая точка задается как , а вторая как . При таком подходе сам пользователь отвечает за согласованность переменных: при инициализации, копировании, при вычислениях, при передачу в функции и тому подобное. Последнее не удобно и в большинстве случаев ведет к ошибкам.
Язык Си позволяет задавать такие объекты естественным образом, что возлагает большую часть согласованности при взаимодействии с объектом на сам язык. Последние позволяет пользователю сконцентрироваться на самой сути программы. Языковая конструкции языка Си заключается в следующем:
Внутри фигурных скобок пишутся объявления переменных (определять их там нельзя!), которые называются полями структуры. Поля описывают сложные объект. В данном случае они являются координатами точки.
Поля в обще говоря могут быть разных типов. Более того, они, как будет показано нижу, могу в свою очередь быть тоже типом некой структуры. Считается, что поля задают сам объект в целом.
Когда нужный тип данных определен объявления переменных можно записать следующим образом:
В первой строчке была объявлена переменная p, а во второй строчке объявлено сразу две переменные p0 и p1. Все перечисленные переменные (p, p0 и p1) имеют тип struct point_2d, т.е. являются структурами (составными объектами) с именем (типом) point_2d.
Последнее очень похоже на объявления переменных стандартных типов. Разница как раз заключается в том, что тип в данном случае struct point_2d. Можно такое название везде в программе и использовать, но это не всегда удобно (слишком длинно). Поэтому его принято сократить за счет использования конструкции переопределения.
Переопределение Заметим, что при объявлении приходится всегда писать вначале термин struct до название самого нашего типа (point_2d). Этого можно избежать использую другую языковую конструкцию языка Си: typedef . Она позволяет заменять любой сложный тип на более короткое название, сокращение:
В последнем примере сложный тип (целочисленный массив из 5 элементов) заменяется на некое обозначение (myintarray). Последнее позволяет далее в программе использовать именно его, а не громоздкое первоначальное (в котором легко ошибиться и в какой-то из очередных объявлений вместо 5 написать, например, 4). В частности, когда решено изменить размер массива (придется везде поменять 5 на, например, 4). Последнее означает, что хорошей практикой проектирования программного обеспечения является использование конструкции typedef.
По аналогии с выше написанном, рассматриваемую конструкцию можно применить и к структурам:
Последнее явно читабельнее и удобнее ранее показанного способа объявления переменных с типом структура.
Вложенные объявления Объекты в Си можно определять не только посредством встроенных типов данных, но и через уже ранее определенные типы. Объект типа прямая (отрезок) можно задать как:
В данном определении структуры задается объект отрезок двумя точками. Также добавлено ещё одно поле weight с отличным от предыдущих типом double. Последним показана возможность использования в структурах полей с разным типом данных.
Этим полем можно задавать, например, вес (важность данного отрезка). Последнее важно например в задачах на графах.
В предыдущем подразделе было показано как описывать свой тип данных и как потом создавать соответствующего типа переменные.
В первом параграфе будет показано как инициализировать (задавать первичное значение) переменным, во втором как выполнять простейшие вычисления над переменными имеющие тип структура.
Инициализация Переменные стандартных типов, как мы знаем, можно инициализировать в момент объявления:
Переменные являющиеся структурами такая возможность тоже имеется. В конце концов все сводится к:
Таким образом значение рассматриваемых переменных задается списком значений, задаваемый парными фигурных скобок. Если какое либо из значений предполагает структуру, то оно задается соответствующим типом: либо ранее объявленной переменной (см. первое поле в стр. 3), либо как и ранее фигурными скобками (см. первое и второе поле в стр. 2, а также второе поле в стр. 3), последнее означает, что фигурные скобки будут вложенными.
Простые вычисления Для начала покажем как выполнять простейшие вычисления, а именно – вычисления над полями сложных переменных. Все конечно сводится к переменной имеющий встроенный тип, например, числовой тип.
Пусть, например, программа вычисляет параллельный сдвиг точки:
Как уже ранее отмечалось в данном случае пользователю самому приходится помнить из чего составлен каждый из объектов. В данном случае, что точки составлены из компонент.
В случае если данные уже определены, то можно переписать этот код как:
Такой подход явно более удобен.
В ещё более явно это проявляется, когда нужно копировать весь объект. Например
Помимо вычислений важным является и ввод вывод сложных объектов. Иначе как и ранее было уже отмечено, будет сложно увидеть результат работы программы. В принципе все что было уже сказано ранее достаточно для понимания того как это делать, так как когда происходит переход к полю с базовым типом оно уже ничем не отличается от обычной переменной. А для них все было уже рассказано.
Но тем не менее покажем как это делать.
Вывод данных Стандартные функции вывода (printf и тому подобное) естественно ничего не знают о нашем типе данных. Поэтому и вывести его самостоятельно не смогут:
Для того чтобы они напечатались необходимо перейти к соответствующим полям самостоятельно:
Ввод данных Продолжая предыдущее, такой же подход необходимо помнить при вводе данных:
Упражнение. Реализовать сортировку по какому-либо из полей.
Вызов функций...
Вызов функции Допустим нужно вычислить расстояние до точки. В случае единственной точки:
В случае если их большей одной необходимо помнить какие переменные между собой соотносятся. Например расстояние до первой точки ищется как:
При вызове функции необходимо будет передать все поля объекта и убедиться что они относятся к одному объекту.
Это уже создает нагромождение переменных, что увеличит шанс ошибки. Тем более, если нужно будет передать больше одной точки:
Достаточно легко ошибиться даже если использовать структуры.
Выходом являются структуры. Они позволяют передать объект как единое целое, что избавляет от ошибок связанных с полями.
Указатели Указатели нужны для сокращения объема передаваемой памяти:
Вместо точки (.) используется указатель (->).
Упражнение. Реализовать функцию swap.