Язык С

Поля


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

#DEFINE KEYWORD 01 #DEFINE EXTERNAL 02 #DEFINE STATIC 04

(числа должны быть степенями двойки). Тогда обработка битов сведется к "жонглированию битами" с помощью операций сдвига, маскирования и дополнения, описанных нами в главе 2. Некоторые часто встречающиеся идиомы:

FLAGS \!= EXTERNAL \! STATIC;

включает биты EXTERNAL и STATIC в FLAGS, в то время как

FLAGS &= \^(еXTERNAL \! STATIC);

их выключает, а

IF ((FLAGS & (EXTERNAL \! STATIC)) == 0) ...

истинно, если оба бита выключены. Хотя этими идиомами легко овладеть, язык "C" в качестве альтернативы предлагает возможность определения и обработки полей внутри слова непосредственно, а не посредством побито- вых логических операций. Поле - это набор смежных битов внутри одной переменной типа INT. Синтаксис определения и обработки полей основывается на структурах. Например, сим- вольную таблицу конструкций #DEFINE, приведенную выше, можно бы было заменить определением трех полей:

STRUCT \( UNSIGNED IS_KEYWORD : 1; UNSIGNED IS_EXTERN : 1; UNSIGNED IS_STATIC : 1; \) FLAGS;


Здесь определяется переменная с именем FLAGS, которая содер- жит три 1-битовых поля. Следующее за двоеточием число задает ширину поля в битах. Поля описаны как UNSIGNED, чтобы под- черкнуть, что они действительно будут величинами без знака. На отдельные поля можно ссылаться, как FLAGS.IS_STATIE, FLAGS. IS_EXTERN, FLAGS.IS_KEYWORD И т.д., то есть точно так же, как на другие члены структуры. Поля ведут себя подобно небольшим целым без знака и могут участвовать в арифметичес- ких выражениях точно так же, как и другие целые. Таким обра- зом, предыдущие примеры более естественно переписать так:

FLAGS.IS_EXTERN = FLAGS.IS_STATIC = 1;

для включения битов;

FLAGS.IS_EXTERN = FLAGS.IS_STATIC = 0;

для выключения битов;

IF (FLAGS.IS_EXTERN == 0 &&FLAGS.IS_STATIC == 0)...

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

При работе с полями имеется ряд моментов, на которые следует обратить внимание. По-видимому наиболее существенным является то, что отражая природу различных аппаратных сред- ств, распределение полей на некоторых машинах осуществляется слева направо, а на некоторых справа налево. Это означает, что хотя поля очень полезны для работы с внутренне опреде- ленными структурами данных, при разделении внешне определяе- мых данных следует тщательно рассматривать вопрос о том, ка- кой конец поступает первым. Другие ограничения, которые следует иметь в виду: поля не имеют знака; они могут храниться только в переменных типа INT (или, что эквивалентно, типа UNSIGNED); они не являются массивами; они не имеют адресов, так что к ним не применима операция &.




    Содержание раздела