В этом разделе рассматриваются модифицируемые, перечислимые и структурные типы данных языка Си.
9.1. Имена с модификаторамиИспользование модификаторов при описании данных позволяет придавать объявлениям специальный смысл. Информация, которую несут модификаторы, используется компилятором при генерации кода.
Модификаторы cdecl,pascal,interrupt воздействуют на идентификатор и могут быть записаны непосредственно рядом c ним:
<модификатор> тип список;
тип <модификатор> список;
В ряде случаев допускаются оба варианта.
Модификаторы const,volatile,near,far,huge воздействуют либо на идентификатор, либо на указатель, расположенный непосредственно справа. Если справа расположен идентификатор, модифицируется тип именуемого объекта. Если справа расположен признак указателя "*", то преобразуется тип объекта, адресуемого указателем на модифицированный тип. Таким образом, конструкция "модификатор *" обозначает указатель на модифицированный тип.
Например, int const*t; - это указатель на const int, который можно переназначать, тогда как int * const t; — это const указатель на int и переназначить его нельзя.
|
|
char *const str = "Строка текста";
str = "Другая строка";
Последний оператор недопустим, т. к. пытается переназначить const указатель. Тем не менее, допустим оператор
strcpy(str,"Строка");
Последнее было бы невозможно для указателя видаchar const *str = "Строка текста";т. к. он указывает на неизменяемую строку.
Модификатор volatile несет противоположный смысл. Он подчеркивает, что значение переменной может быть изменено не только исполняемой программой, но и некоторым внешним воздействием, например, программой обработки прерываний или обменом с внешним устройством. Фактически, объявление volatile "предупреждает" компилятор, что модифицируемая переменная может измениться в любой момент и не следует делать предположений относительно стабильности значения соответствующего объекта в памяти. Для выражений, содержащих объекты volatile, не будут применяться методы оптимизации кода, такие объекты не будет загружаться в регистры процессора.
Модификатор pascal в применении к идентификатору означает, что он преобразуется к верхнему регистру и к нему не добавляется символ подчеркивания. В применении к имени функции модификатор оказывает влияние также на передачу аргументов. В этом случае аргументы пересылаются в стек в прямомпорядке, т. е., первым отправляется первый аргумент. По умолчанию в Си принято пересылать аргументы в обратном порядке. Существует также настройка компиляции, которая присваивает всем функциям и указателям на функции тип pascal. Такие функции могут вызываться из программы на Паскале. При этом может потребоваться, чтобы некоторые функции и указатели на функции применяли вызывающую последовательность, принятую в Си, а их имена имели принятый для идентификаторов Си вид. В этом случае соответствующие объявления делаются с модификатором cdecl. Все функции в стандартных включаемых файлах объявлены с модификатором cdecl.Модификатор interrupt предназначен для объявления функций‑обработчиков прерываний. Для такой функции генерируется дополнительный код для сохранения и восстановления состояния регистров процессора. Модификатор не может использоваться совместно с near, far или huge. Приведем пример использования volatile и interrupt.volatile int t;
|
|
void interrupt time() { t++; }
wait(int i) {
t=0;
while (t < i);
}
Без объявления t как volatile оптимизирующий компилятор мог бы вынести за пределы цикла сравнение t и i, поскольку обе они не меняются в теле цикла. Это привело бы к зацикливанию.Модификаторы near, far, huge оказывают действие на работу с адресами объектов. Формат указателей, принятый в данный момент по умолчанию, определяется используемой моделью памяти (см. п. 11). Однако можно объявить указатель с форматом, отличным от действующего по умолчанию. Это делается с помощью явного указания ключевого слова near, far или huge:
char far *p = 0xB8000000ul; //"длинный" указатель сегмент+смещение