Функции Члены Структур В Языке Си

Функции Члены Структур В Языке Си




⚡ 👉🏻👉🏻👉🏻 ИНФОРМАЦИЯ ДОСТУПНА ЗДЕСЬ ЖМИТЕ 👈🏻👈🏻👈🏻

































Функции Члены Структур В Языке Си

Структура - это совокупность переменных, объединенных одним именем, предоставляющая общепринятый способ совместного хранения информации. Объявление структуры приводит к образованию шаблона, используемого для создания объектов структуры. Переменные, образующие структуру, называются членами структуры. (Члены структуры также часто называются элементами или полями.)

Обычно все члены структуры связаны друг с другом. Например, информация об имени и адресе, находящаяся в списке рассылки, обычно представляется в виде структуры. Следующий фрагмент кода объявляет шаблон структуры, определяющий имя и адрес. Ключевое слово struct сообщает компилятору об объявлении структуры.

struct addr {
char name[30];
char street [40]; char city[20];
char state[3];
unsigned long int zip;
};

Объявление завершается точкой с запятой, поскольку объявление структуры - это оператор. Имя структуры addr идентифицирует структуру данных и является спецификатором типа. Имя структуры часто используют как ярлык.

На данный момент на самом деле не создано никакой переменной. Определена только форма данных. Для объявления настоящей переменной, соответствующей данной структуре, следует написать:

struct addr addr_info;

В данной строке происходит объявление переменной addr_info типа addr. При объявлении структуры определяется переменная смешанного типа. До тех пор, пока не будет объявлена переменная данного типа, она не будет существовать.

Когда объявлена структурная переменная, компилятор автоматически выделяет необходимый участок памяти для размещения всех ее членов. Рис. показывает размещение addr_info в памяти.

При объявлении структуры можно одновременно объявить одну или несколько переменных.

Например:

struct addr {
char name[30];
char street[40];
char city[20];
char state[3];
unsigned long int zip;
} addr_info; binfo, cinfo;

объявляет структуру addr и объявляет переменные addr_info, binfo, cinfo данного типа.

Важно понять, что каждая вновь создаваемая структурная переменная содержит свои собственный копии переменных, образующих структуру. Например, поле zip переменной binfo отделено от поля zip переменной cinfo. Фактически, единственная связь между binfo и cinfo заключается в том, что они обе являются экземплярами одного типа структуры. Больше между ними нет связи.

Если необходима только одна структурная переменная, то нет необходимости в ярлыке структуры. Это означает, что

struct {
char name[30];
char street[40];
char city[20];
char state[3];
unsigned long int zip;
} addr_info;

объявляет одну переменную addr_info с типом, определенным предшествующей ей структурой. Стандартный вид объявления структуры следующий:

struct ярлык {
тип имя переменной;
тип имя переменной;
тип имя переменной;
} структурные переменные;

ярлык - это имя типа структуры, а не имя переменной. Структурные переменные - это разделенный запятыми список имен переменных. Следует помнить, что или ярлык, или структурные переменные могут отсутствовать, но не оба.


Структура — это одна или несколько переменных (возможно, различных типов),
которые для удобства работы с ними сгруппированы под одним именем. (В
некоторых языках, в частности в Паскале, структуры называются записями.)
Структуры помогают в организации сложных данных (особенно в больших
программах), поскольку позволяют группу связанных между собой переменных
трактовать не как множество отдельных элементов, а как единое целое.


Распространенный пример структуры — строка платежной ведомости. Она содержит
такие сведения о служащем, как его полное имя, адрес, номер карточки
социального страхования, зарплата и т.д. Некоторые из этих характеристик сами
могут быть структурами: например, полное имя состоит из нескольких компонент
(фамилии, имени и отчества); аналогично адрес, и даже зарплата. Другой пример
(более типичный для Си) — из области графики: точка есть пара координат,
прямоугольник есть пара точек и т.д.


Главные изменения, внесенные стандартом ANSI в отношении структур, — это
введение для них операции присваивания. Структуры могут копироваться, над
ними могут выполняться операции присваивания, их можно передавать функциям в
качестве аргументов, а функции могут возвращать их в качестве результатов. В
большинстве компиляторов уже давно реализованы эти возможности, но теперь они
точно оговорены стандартом. Для автоматических структур и массивов теперь
также допускается инициализация.


Сконструируем несколько графических структур. В качестве основного объекта
выступает точка с координатами x и y , и пусть они имеют тип int .


Указанные две компоненты можно поместить в структуру, описанную, например,
следующим образом:


Описание структуры начинается с ключевого слова struct и содержит список
деклараций, заключенный в фигурные скобки. За словом struct может следовать
имя, называемое тегом (от английского слова tag — ярлык, этикетка. — Примеч.
пер.) структуры ( point в нашем случае). Тег дает название структуре данного
вида и далее может служить кратким обозначением той части декларации, которая
заключена в фигурные скобки.


Перечисленные в структуре переменные называются членами. Имена членов и тегов
без каких-либо коллизий могут совпадать с именами обычных переменных (т.е. не
членов), так как они всегда различимы по контексту. Более того, одни и те же
имена членов могут встречаться в разных структурах, хотя, если следовать
хорошему стилю программирования, лучше одинаковые имена давать только близким
по смыслу объектам.


Декларация структуры — это тип. За правой фигурной скобкой, закрывающей
список членов, могут следовать переменные точно так же, как они могут быть
указаны после названия любого базового типа. Таким образом, запись


с точки зрения синтаксиса аналогична записи


в том смысле, что каждая декларирует x , y и z как переменные указанного типа.
Обе записи приведут к тому, что где-то будет выделена память соответствующего
размера.


Декларация структуры, не содержащей списка переменных, не резервирует памяти:
она просто описывает шаблон, или образец структуры. Однако если структура
имеет тег, то этим тегом далее можно пользоваться при определении структурных
объектов. Например, с помощью заданной выше декларации структуры point строка


определяет структурную переменную pt типа struct point . Структурную
переменную при ее определении можно инициализировать, формируя список
инициализаторов ее членов в виде константных выражений:


Инициализировать автоматические структуры можно также присваиванием или
обращением к функции, возвращающей результат в виде структуры
соответствующего типа.


Доступ к отдельному члену структуры осуществляется посредством конструкции
вида:


Оператор доступа к члену структуры « . » соединяет имя структуры и имя члена.
Чтобы напечатать, например, координаты точки pt , годится следующее обращение
к printf :


Другой пример: чтобы вычислить расстояние от начала координат (0, 0) до pt ,
можно написать


Структуры могут быть вложены друг в друга. Одно из возможных представлений
прямоугольника — это пара точек на углах одной из его диагоналей:


Структура rect содержит две структуры point . Если мы декларируем screen как


ссылается на координату x точки pt1 из screen .


Единственно возможные операции над структурами — это их копирование,
присваивание, взятие адреса с помощью & и осуществление доступа к ее членам.
Передача структур функциям в качестве аргументов и возврат их от функций в
виде результата также относятся к операциям копирования и присваивания.
Структуры нельзя сравнивать. Инициализировать структуру можно списком
константных значений ее членов; автоматическую структуру можно
инициализировать также присваиванием.


Чтобы лучше познакомиться со структурами, напишем несколько функций,
манипулирующих точками и прямоугольниками. Возникает вопрос: а как
передавать функциям названные объекты? Существует по крайней мере три
подхода: передавать компоненты по отдельности, передавать всю структуру
целиком и передавать указатель на структуру. Каждый подход имеет свои плюсы
и минусы.


Первая функция, makepoint , получает два целых значения и возвращает структуру
point .


Заметим: никакой путаницы из-за того, что имя аргумента совпадает с именем
члена структуры не возникает; более того, одно и то же имя подчеркивает
родство обозначаемых им объектов.


Теперь с помощью makepoint можно выполнять динамическую инициализацию любой
структуры или формировать структурные аргументы для той или иной функции:


Нам может понадобиться ряд функций, реализующих различные операции над
точками. В качестве примера рассмотрим следующую функцию:


Здесь оба аргумента и возвращаемое значение — структуры. Мы увеличиваем
компоненты прямо в p1 и не используем для этого временной переменной, чтобы
подчеркнуть, что структурные параметры передаются по значению так же, как и
любые другие.


В качестве другого примера приведем функцию ptinrect , которая проверяет:
находится ли точка внутри прямоугольника, относительно которого мы принимаем
соглашение, что в него входят его левая и нижняя стороны, но не входят
верхняя и правая.


Здесь предполагается, что прямоугольник представлен в стандартном виде, т.е.
координаты точки pt1 меньше соответствующих координат точки pt2 . Следующая
функция гарантирует получение прямоугольника в каноническом виде.


Если функции передается большая структура, то, чем копировать ее целиком,
эффективнее передать указатель на нее. Указатели на структуры ничем не
отличаются от указателей на обычные переменные. Декларация


сообщает, что pp есть указатель на структуру типа struct point . Если pp
ссылается на структуру point , то *pp есть сама структура, а (*pp).x и (*pp).y
— ее члены. Используя указатель pp , мы могли бы написать


Скобки в (*pp).x необходимы, поскольку приоритет оператора выше, чем
приоритет * . Выражение *pp.x будет проинтерпретировано как *(pp.x) , что
неверно, поскольку pp.x не является указателем.


Указатели на структуры используются весьма часто, поэтому для доступа к ее
членам была придумана еще одна, более короткая форма записи. Если p —
указатель на структуру, то


есть ее отдельный член. (Оператор -> состоит из знака - , за которым сразу
следует знак > .) Поэтому printf можно переписать в виде


Оба оператора и -> выполняются слева направо. Таким образом, при наличии
декларации


следующие четыре выражения будут эквивалентны:


Операторы доступа к членам структуры . и -> вместе с операторами вызова
функции () и индексации массива [] занимают самое высокое положение в
иерархии приоритетов и выполняются раньше любых других операторов. Например,
если задана декларация


увеличит на 1 значение члена структуры len , а не указатель p , поскольку в
этом выражении как бы неявно присутствуют скобки: ++(p->len) . Чтобы изменить
порядок выполнения операций, нужны явные скобки. Так, в (++p)->len , прежде
чем взять значение len , программа продвинет указатель p . В (p++)->len
указатель p увеличится после того, как будет взято значение len (в последнем
случае скобки не обязательны).


По тем же правилам *p->str обозначает содержимое объекта, на который
ссылается str ; *p->str++ продвинет указатель str после получения значения
объекта, на который он указывал (как и в выражении вида *s++ ); ( *p->str)++
увеличит значение объекта, на который ссылается str ; *p++->str продвинет p
после того, как будет получено то, на что указывает str .


Рассмотрим программу, определяющую число вхождений каждого ключевого слова в
текст Си-программы. Нам нужно уметь хранить ключевые слова в виде массива
стрингов и счетчики ключевых слов в виде массива целых. Один из возможных
вариантов — это иметь два параллельных массива:


Однако именно тот факт, что они параллельны, подсказывает нам другую
организацию хранения — через массив структур. Каждое ключевое слово можно
описать парой характеристик


Такие пары составляют массив. Декларация


описывает структуру типа key и определяет массив keytab , каждый элемент
которого есть структура этого типа и которому где-то будет выделена память.
Это же можно записать и по-другому:


Так как keytab содержит постоянный набор имен, его легче всего сделать
внешним массивом и инициализировать один раз в момент определения.
Инициализация структур аналогична ранее демонстрировавшимся инициализациям —
за определением следует список инициализаторов, заключенный в фигурные
скобки:


Инициализаторы задаются парами, чтобы соответствовать конфигурации структуры.
Строго говоря, пару инициализаторов для каждой отдельной структуры следовало
бы заключить в фигурные скобки, как, например, в


Однако, когда инициализаторы — простые константы или цепочки литер, и все они
имеются в наличии, во внутренних скобках нет необходимости. Число элементов
массива keytab будет вычислено по количеству инициализаторов, поскольку они
представлены полностью, а внутри квадратных скобок [] ничего не задано.


Программа подсчета ключевых слов начинается с определения keytab . Программа
main читает ввод, многократно обращаясь к функции getword и получая на каждом
ее вызове очередное слово. Каждое слово ищется в keytab . Для этого
используется функция бинарного поиска, которую мы написали в гл. 3 . Список
ключевых слов должен быть упорядочен в алфавитном порядке.


Чуть позже мы рассмотрим функцию getword , а сейчас нам достаточно знать, что
при каждом ее вызове получается очередное слово, которое запоминается в
массиве, заданном первым аргументом.


NKEYS — количество ключевых слов в keytab . Хотя мы могли бы подсчитать число
таких слов вручную, гораздо легче и безопасней сделать это с помощью машины,
особенно если список ключевых слов может быть изменен. Одно из возможных
решений — поместить в конец списка инициализаторов пустой указатель ( NULL ) и
затем перебирать в цикле элементы keytab , пока не встретится концевой
элемент.


Но возможно и более простое решение. Поскольку размер массива полностью
определен во время компиляции и равен произведению количества элементов
массива на размер его отдельного элемента, число элементов массива можно
вычислить по формуле


В Си имеется унарный оператор sizeof , который работает во время компиляции.
Его можно применять для вычисления размера любого объекта. Выражения


выдают целые значения, равные размеру указанного объекта или типа в байтах.
(Строго говоря. sizeof выдает беззнаковое целое, тип которого size_t
определен в головном файле .) Что касается объекта, то это может
быть переменная, массив или структура. В качестве имени типа может выступать
имя базового типа ( int , double ...) или имя производного типа, например,
структуры или указателя.


В нашем случае, чтобы вычислить количество ключевых слов, размер массива надо
поделить на размер одного элемента. Указанное вычисление используется в
инструкции #define для установки значения NKEYS :


Этот же результат можно получить другим способом — поделить размер массива на
размер какого-то его конкретного элемента:


Преимущество такого рода записей в том, что их не надо корректировать при
изменении типа.


Поскольку препроцессор не обращает внимания на имена типов, оператор sizeof
нельзя применять в #if . Но в #define выражение препроцессором не вычисляется,
так что предложенная нами запись допустима.


Теперь поговорим о функции getword . Мы написали getword в несколько более
общем виде, чем требуется для нашей программы, но она от этого не стала
заметно сложнее. Функция getword берет из входного потока следующее «слово».
Под словом понимается цепочка букв-цифр, начинающаяся с буквы, или отдельная
непробельная литера. По концу файла функция выдает EOF , в остальных случаях
ее значением является код первой литеры слова или код отдельной литеры, если
она не буква.


Функция getword обращается к getch и ungetch , которые мы написали в гл. 4 .
При завершении набора букв-цифр оказывается, что getword взяла лишнюю литеру.
Обращение к ungetch позволяет вернуть ее назад во входной поток. В getword
используются также isspace — для пропуска пробельных литер, isalpha — для
идентификации букв и isalnum — для распознавания букв-цифр. Все они описаны в
стандартном головном файле .


Наша версия getword не обрабатывает должным образом знак
подчеркивания, стринговые константы, комментарии и управляющие строки
препроцессора. Напишите более совершенный вариант программы.


Для иллюстрации некоторых моментов, касающихся указателей на структуры и
массивов структур, перепишем программу подсчета ключевых слов, пользуясь для
получения элементов массива вместо индексов указателями.


Внешняя декларация массива keytab остается без изменения, а main и binsearch
нужно модифицировать.


Некоторые детали этой программы требуют пояснений. Первое, описание функции
binsearch должно отражать тот факт, что она возвращает указатель на struct
key , а не целое; соответствующие изменения коснулись как прототипа функции,
так и ее заголовка. Если binsearch находит слово, то она выдает указатель на
него, в противном случае она возвращает NULL . Второе, к элементам keytab
доступ осуществляется в нашей программе через указатели. Это потребовало
значительных изменений в binsearch . Инициализаторами для low и high теперь
служат указатели на начало и на место сразу после конца массива. Вычисление
положения среднего элемента с помощью формулы


не годится, поскольку указатели нельзя складывать. Однако к ним можно
применить операцию вычитания, и так как high-low есть число элементов,
присваивание


установит в mid указатель на элемент, лежащий посередине между low и high .


Самое важное при переходе на новый вариант программы — сделать так, чтобы не
генерировались неправильные указатели и не было попыток обращений за пределы
массива. Проблема в том, что и &tab[-1] , и &tab[n] находятся вне границ
массива. Первый адрес определенно неверен, нельзя также осуществить доступ и
по второму адресу. По правилам языка, однако, гарантируется, что адрес ячейки
памяти, следующей сразу за концом массива (т.е. &tab[n] ), в арифметике с
указателями воспринимается правильно.


Если p — указатель на структуру, то при выполнении операций с p учитывается
размер структуры. Поэтому p++ увеличит p на такую величину, чтобы выйти на
следующий структурный элемент массива, а проверка условия вовремя остановит
цикл.


Не следует, однако, полагать, что размер структуры равен сумме размеров ее
членов. Вследствие выравнивания объектов разной длины в структуре могут
появляться безымянные «дыры». Так, например, если переменная типа char
занимает один байт, а int — четыре байта, то для структуры


может потребоваться восемь байт, а не пять. Оператор sizeof возвращает
правильное значение.


Наконец, несколько слов относительно формата программы. Если функция
возвращает значение сложного типа, как, например, в нашем случае указатель на
структуру:


То имя функции «высмотреть» оказывается совсем не просто. В таких случаях
иногда пользуются записью вида:


Какой форме отдать предпочтение — дело вкуса. Выберите ту, которая больше
всего вам нравится.


Предположим, что мы хотим решить более общую задачу — написать программу,
подсчитывающую частоту встречаемости для любых слов
Шикарная русская девушка обожает сосать и трахаться
Порно Онлайн Со Зрелами
Порно Видео Русских Молодых Студенток

Report Page