Использование в качестве членов структур массивов и структур



 

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

Массив, используемый в качестве члена структуры, обрабатывается вполне ожидаемым способом. Рассмотрим такую структуру.

 

 

Чтобы обратиться к элементу массива nums с "координатами" 3,7 в структуре var типа stype , следует записать такой код:

 

 

Как показано в этом примере, если массив является членом структуры, то для доступа к элементам этого массива индексируется имя массива, а не имя структуры.

Если некоторая структура является членом другой структуры, она называется вложенной структурой. В следующем примере структура addr вложена в структуру emp .

 

 

Здесь структура emp имеет два члена. Первым членом является структура типа addr , которая будет содержать адрес служащего. Вторым членом является переменная wage , которая хранит его оклад. При выполнении следующего фрагмента кода полю zip структуры address , которая является членом структуры worker , будет присвоен почтовый индекс 98765 :

 

 

Как видите, члены структур указываются слева направо, от самой крайней внешней до самой дальней внутренней.

Структура также может содержать в качестве своего члена указатель на эту же структуру. И в самом деле для структуры вполне допустимо содержать член, который является указателем на нее саму. Например, в следующей структуре переменная sptr объявляется как указатель на структуру типа mystruct , т.е. на объявляемую здесь структуру.

 

 

Структуры, содержащие указатели на самих себя, часто используются при создании таких структур данных, как связные списки . По мере изучения языка C++ вы встретите приложения, в которых применяются подобные вещи.

 

Сравнение С‑ и С++‑структур

 

С++‑структуры – потомки С‑структур. Следовательно, любая С‑структура также является действительной С++‑структурой. Между ними, однако, существуют важные различия. Во‑первых, как будет показано в следующей главе, С++‑структуры имеют некоторые уникальные атрибуты, которые позволяют им поддерживать объектно‑ориентированное программирование. Во‑вторых, в языке С структура не определяет в действительности новый тип данных. Этим может "похвалиться" лишь С++‑структура. Как вы уже знаете, определяя структуру в C++, вы определяете новый тип, который называется по имени этой структуры. Этот новый тип можно использовать для объявления переменных, определения значений, возвращаемых функциями, и т.п. Однако в С имя структуры называется ее тегом (или дескриптором ). А тег сам по себе не является именем типа.

Чтобы понять это различие, рассмотрим следующий фрагмент С‑кода.

 

 

Обратите внимание на то, что приведенное выше определение структуры в точности такое же, как в языке C++. Теперь внимательно рассмотрите объявление структурной переменной svar . Оно также начинается с ключевого слова struct . В языке С после определения структуры для полного задания типа данных все равно нужно использовать ключевое слово struct совместно с тегом этой структуры (в данном случае с идентификатором C_struct ).

Если вам придется преобразовывать старые С‑программы в код C++, не беспокойтесь о различиях между С‑ и С++‑структурами, поскольку C++ по‑прежнему принимает С‑ориентированные объявления. Например, предыдущий фрагмент С‑кода корректно скомпилируется как часть любой С++‑программы. С точки зрения компилятора C++ в объявлении переменной svar всего лишь избыточно использовано ключевое слово struct , без которого в C++ можно обойтись.

 

Битовые поля структур

 

Битовое полеэто бит‑ориентированный член структуры.

В отличие от многих других компьютерных языков, в C++ предусмотрен встроенный способ доступа к конкретному разряду байта. Побитовый доступ возможен путем использования битовых полей. Битовые поля могут оказаться полезными в различных ситуациях. Приведем всего три примера. Во‑первых, если вы имеете дело с ограниченным объемом памяти, можно хранить несколько булевых (логических) значений в одном байте. Во‑вторых, некоторые интерфейсы устройств передают информацию, закодированную именно в битах. И, в‑третьих, существуют подпрограммы кодирования, которым нужен доступ к отдельным битам в рамках байта. Реализация всех этих функций возможна с помощью поразрядных операторов, как было показано в предыдущей главе, но битовое поле может сделать вашу программу более прозрачной и читабельной, а также повысить ее переносимость.

Метод, который использован в языке C++ для доступа к битам, основан на применении структур. Битовое поле – это в действительности специальный тип члена структуры, который определяет свой размер в битах. Общий формат определения битовых полей таков.

 

 

Здесь элемент тип означает тип битового поля, а элемент длина – количество битов в этом поле. Битовое поле должно быть объявлено как значение целочисленного типа или перечисления. Битовые поля длиной 1 бит объявляются как значения типа без знака (unsigned ), поскольку единственный бит не может иметь знакового разряда.

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

 

 

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

 

 

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

 

 

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

 

 

Как видно из этих примеров, доступ к каждому битовому полю можно получить с помощью оператора "точка" . Но если общий доступ к структуре осуществляется через указатель, необходимо использовать оператор "‑>" .

Следует иметь в виду, что совсем необязательно присваивать имя каждому битовому полю. Это позволяет обращаться только к нужным битам, "обходя" остальные. Например, если вас интересуют только биты cts и dsr , вы могли бы объявить структуру status_type следующим образом.

 

 

Обратите здесь внимание на то, что биты после последнего именованного dsr нет необходимости вообще упоминать.

В структуре можно смешивать "обычные" члены с битовыми полями. Вот пример.

 

 

Эта структура определяет запись по каждому служащему, в которой используется только один байт для хранения трех элементов информации: статус служащего, характер оплаты его труда (почасовая оплата или твердый оклад) и налоговая ставка. Без использования битовых полей для хранения этой информации пришлось бы занять три байта.

Использование битовых полей имеет определенные ограничения. Программист не может получить адрес битового поля или ссылку на него. Битовые поля нельзя хранить в массивах. Их нельзя объявлять статическими. При переходе от одного компьютера к другому невозможно знать наверняка порядок следования битовых полей: справа налево или слева направо. Это означает, что любая программа, в которой используются битовые поля, может страдать определенной зависимостью от марки компьютера. Возможны и другие ограничения, связанные с особенностями реализации компилятора C++, поэтому имеет смысл прояснить этот вопрос в соответствующей документации.

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

 

Объединения

 

Объединение состоит из нескольких переменных, которые разделяют одну и ту же область памяти.

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

 

 

Объявление объединения начинается с ключевого слова union .

Здесь объявляется объединение, в котором значение типа short int и значение типа char разделяют одну и ту же область памяти. Необходимо сразу же прояснить один момент: невозможно сделать так, чтобы это объединение хранило и целочисленное значение, и символ одновременно, поскольку переменные i и ch накладываются (в памяти) друг на друга. Но программа в любой момент может обрабатывать информацию, содержащуюся в этом объединении, как целочисленное значение или как символ. Следовательно, объединение обеспечивает два (или больше) способа представления одной и той же порции данных. Как видно из этого примера, объединение объявляется с помощью ключевого слова union .

Как и при использовании структур, при объявлении объединения не определяется ни одна переменная. Переменную можно объявить, разместив ее имя в конце объявления либо воспользовавшись отдельной инструкцией объявления. Чтобы объявить переменную объединения именем u_var типа utype , достаточно записать следующее:

 

 

В переменной объединения u_var как переменная i типа short int , так и символьная переменная ch разделяют одну и ту же область памяти. (Безусловно, переменная i занимает два байта, а символьная переменная ch использует только один.) Как переменные i и ch разделяют одну область памяти, показано на рис. 10.2.

 

 

При объявлении объединения компилятор автоматически выделяет область памяти, достаточную для хранения в объединении переменных самого большого по объему типа.

Чтобы получить доступ к элементу объединения, используйте тот же синтаксис, который применяется и для структур: операторы "точка" и "стрелка" . При непосредственном обращении к объединению (или посредством ссылки) используется оператор "точка" . Если же доступ к переменной объединения осуществляется через указатель, используется оператор "стрелка" . Например, чтобы присвоить букву 'А' элементу ch объединения u_var , достаточно использовать такую запись.

 

 

В следующем примере функции передается указатель на объединение u_var . В теле этой функции с помощью указателя переменной i присваивается значение 10 .

 

 

Поскольку объединения позволяют вашей программе интерпретировать одни и те же данные по‑разному, они часто используются в случаях, когда требуется необычное преобразование типов. Например, следующая программа использует объединение для перестановки двух байтов, которые составляют короткое целочисленное значение. Здесь для отображения содержимого целочисленных переменных используется функция disp_binary() , разработанная в главе 9. (Эта программа написана в предположении, что короткие целочисленные значения имеют длину два байта.)

 

 

При выполнении программа генерирует такие результаты.

 

 

В этой программе целочисленной переменной sb.num присваивается число 15 . Перестановка двух байтов, составляющих это значение, выполняется путем обмена двух символов, которые образуют массив ch . В результате старший и младший байты целочисленной переменной num меняются местами. Эта операция возможна лишь потому, что как переменная num , так и массив ch разделяют одну и ту же область памяти.

В следующей программе демонстрируется еще один пример использования объединения. Здесь объединения связываются с битовыми полями, используемыми для отображения в двоичной системе счисления ASCII ‑кода, генерируемого при нажатии любой клавиши. Эта программа также демонстрирует альтернативный способ отображения отдельных битов, составляющих байт. Объединение позволяет присвоить значение нажатой клавиши символьной переменной, а битовые поля используются для отображения отдельных битов.

 

 

Вот как выглядит один из возможных вариантов выполнения этой программы.

 

 

Важно! Поскольку объединение предполагает, что несколько переменных разделяют одну и ту же область памяти, это средство предоставляет программисту возможность хранить информацию, которая (в зависимости от ситуации) может содержать различные типы данных, и получать доступ к этой информации. По сути, объединения обеспечивают низкоуровневую поддержку принципов полиморфизма. Другими словами, объединение обеспечивает единый интерфейс для нескольких различных типов данных, воплощая таким образом концепцию "один интерфейс – множество методов" в своей самой простой форме.

 

Анонимные объединения

 

Анонимные объединения позволяют объявлять переменные, которые разделяют одну и ту же область памяти.

В C++ предусмотрен специальный тип объединения, который называется анонимным . Анонимное объединение не имеет наименования типа, и поэтому объект такого объединения объявить невозможно. Но анонимное объединение сообщает компилятору о том, что его члены разделяют одну и ту же область памяти. При этом обращение к самим переменным объединения происходит непосредственно, без использования оператора "точка" . Рассмотрим такой пример.

 

 

Эта программа отображает следующий результат.

 

 

Число 22872 получено в результате помещения символов X и Y в младший и старший байты переменной count соответственно. Как видите, к обеим переменным, входящим в состав объединения, как count , так и ch , можно получить доступ так же, как к обычным переменным, а не как к составляющим объединения. Несмотря на то что они объявлены как часть анонимного объединения, их имена находятся на том же уровне области видимости, что и другие локальные переменные, объявленные на уровне объединения. Таким образом, член анонимного объединения не может иметь имя, совпадающее с именем любой другой переменной, объявленной в той же области видимости.

Анонимное объединение представляет собой средство, с помощью которого программист может сообщить компилятору о своем намерении, чтобы две (или больше) переменные разделяли одну и ту же область памяти. За исключением этого момента, члены анонимного объединения ведут себя подобно любым другим переменным.

 


Дата добавления: 2018-09-22; просмотров: 445; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!