close

Вход

Забыли?

вход по аккаунту

?

C1

код для вставкиСкачать

- 22 -
3. СТРУКТУРА ПРОГРАММЫ
В этом разделе описывается структура исходной программы на
Си и определяются термины, используемые в последующих разделах
руководства при описании языка. По сути, здесь представлен общий
обзор особенностей языка Си, которые в дальнейшем рассмотрены в
деталях.
3.1. Исходная программа
Исходная программа- это совокупность следующих об"ектов:
директив, указаний компилятору, об"явлений и определений. Дирек-
тивы задают действия препроцессора по преобразованию текста прог-
раммы перед компиляцией. Указания компилятору- это команды, вы-
полняемые компилятором во время процесса компиляции. Об"явления
задают имена и атрибуты переменных, функций и типов, используемых
в программе. Определения- это об"явления, определяющие переменные
и функции.
Определение переменной в дополнении к ее имени и типу зада-
ет начальное значение об"явленной переменной. Кроме того, опреде-
ление предполагает распределение памяти для переменной.
Определение функции специфицирует ее структуру, которая
представляет собой смесь из об"явлений и операторов, которые об-
разуют саму функцию. Определение функции также задает имя функ-
ции, ее формальные параметры и тип возвращаемой величины.
Исходная программа может содержать любое число директив,
указаний компилятору, об"явлений и определений. Любой из об"ектов
программы имеет определенный синтаксис, описанный в этом руковод-
стве,и каждая составляющая может появляться в любом порядке, хотя
влияние порядка, в котором следуют переменные и функции может
быть использовано в программе (см. раздел 3.5 "Время жизни и ви-
димость").
Нетривиальная программа всегда содержит более одного опре-
деления функции. Функция определяет действия, выполняемые прог-
раммой.
В следующем примере иллюстрируется простая исходная прог-
рамма на языке Си.
int x = 1; /* Variable definitions */
int y = 2;
extern int printf(char *,...);/* Function declaration */
main () /* Function definition
for main function */
{
int z; /* Variable declarations */
int w;
z = y + x; /* Executable statements */
- 23 -
w = y - x;
printf("z = %d \nw = %d \n", z, x);
}
Эта исходная программа определяет функцию с именем main и
об"являет функцию printf. Переменные x и y задаются своими опре-
делениями. Переменные z и w только об"являются.
3.2. Исходные файлы
Исходные программы могут быть разделены на несколько фай-
лов. Исходный файл Си- это текстовый файл, который содержит часть
или всю исходную программу. Он может, например, содержать только
некоторые функции, требуемые программе. При компиляции исходной
программы каждый из исходных файлов должен быть прокомпилирован
отдельно, а затем обработан сборщиком. Отдельные исходные файлы
перед компиляцией можно соединять в один большой исходный файл
посредством директивы #include.
Исходный файл может содержать любую комбинацию наборов: ди-
ректив, указаний компилятору, об"явлений и определений. Такие
об"екты, как определения функций или большие структуры данных, не
могут разрываться, начинаясь в одном файле и продолжаясь в дру-
гом.
Исходный файл не обязательно должен содержать выполняемые
операторы. Иногда полезно размещать описания переменных в одном
файле с тем, чтобы использовать их путем об"явления ссылок из
других файлов. В этом случае определения становятся легко доступ-
ными для поиска и модификации. Из тех же самых соображений конс-
танты и макросы часто организуют в отдельных #include- файлах и
включают их, если требуется, в исходные файлы.
Директивы исходного файла относятся только к этому исходно-
му файлу и файлам, включающим его (посредством #include). Кроме
того, каждая директива относится только к части файла, которая
следует за ней. Если множество директив должно относиться ко всей
исходной программе, то все исходные файлы должны содержать эти
директивы.
Указания компилятору обычно эффективны для отдельных облас-
тей исходного файла. Специфические действия компилятора, задавае-
мые указаниями, определяются содержанием последних.
Подробнее о действиях отдельных указаний компилятору описа-
но в разделе 8.5 руководства.
В нижеследующем примере исходная программа состоит из двух
исходных файлов. Функции main и max представлены в отдельных фай-
лах. Функция main использует функцию max при своем выполнении.
/***********************************************
Sourse file 1 - main function
***********************************************/
#define ONE 1
#define TWO 2
#define THREE 3
- 24 -
extern int max(int, int); /* Function declaration */
main () /* Function definition */
{
int w = ONE, x = TWO, y = THREE;
int z = 0;
z = max(x,y);
w = max(z,w);
}
/*************************************************
Sourse file 2 - max function
*************************************************/
int max(a,b) /* Function definition */
int a, b;
{
if ( a > b )
return (a);
else
return (b);
}
В первом исходном файле функция max об"явлена без ее опре-
деления. Это об"явление известно как "forward"- об"явление. Опре-
деление функции main включает вызов функции max.
Строки, начинающиеся с символа "номер" (#), являются дирек-
тивами препроцессора. Директивы инструктируют препроцессор о за-
мене в первом исходном файле имен ONE, TWO, THREE на соответству-
ющие им значения. Область действия деректив не распространяется
на второй исходный файл.
Второй исходный файл содержит описание функции max. Это оп-
ределение соответствует вызовам max из первого исходного файла.
3.3 Выполнение программ
Каждая программа содержит главную программную функцию. В Си
главная программная функция должна быть поименована как main.
Функция main служит точкой старта при выполнении программы и
обычно управляет выполнением программы, организуя вызовы других
функций. Программа обычно завершает выполнение по окончанию функ-
ции main, хотя она может завершиться и в других точках, в зависи-
мости от окружающей обстановки.
Исходная программа обычно включает в себя несколько функ-
ций, каждая из которых предназначена для выполнения определенного
задания. Функция main может вызывать эти функции с тем, чтобы вы-
полнить то или иное задание. Функция возвращает управление при
выполнении оператора return или по окончанию самой функции (выход
на конец функции).
Все функции, включая функцию main, могут быть об"явлены с
параметрами. Вызываемые функции получают значения параметров из
вызывающих функций. Значения параметров функции main могут быть
переданы из внешнего окружения. Например, они могут быть переданы
из командной строки.
- 25 -
Соглашение Си требует, чтобы первые два параметра функции
main назывались argc и argv.
Параметр argc определяет общее число аргументов, передавае-
мых функции main. Параметр argv об"является как массив указате-
лей, каждый элемент которого ссылается на строковое представление
аргумента, передаваемого функции main. Третий параметр функции
main (если он есть) традиционно задается с именем envp. Однако Си
не требует этого имени. Параметр envp- это указатель на массив
указателей строковых величин, которые определяют окружение, в ко-
тором выполняется программа.
Операционная система поддерживает передачу значений для
argc, argv, и envp параметров, а пользователь поддерживает зада-
ние значений фактических параметров для функции main. Соглашение
о передаче параметров в большей степени определяется операционной
системой, чем самим языком Си.
Формальные параметры функции должны быть об"явлены во время
описания функции.
3.4. Время жизни и видимость
Концепции "Время жизни" и "Видимость" являются очень важны-
ми для понимания структуры программ на Си. Время жизни переменной
может быть или "глобальным" или "локальным". Об"ект с глобальным
временем жизни характеризуется определенной памятью и значением
на протяжении всей жизни программы. Об"ект с локальным временем
жизни захватывает новую память при каждом входе в "блок", в кото-
ром он определен или об"явлен. Когда блок завершается, локальный
об"ект пропадает, а следовательно пропадает его значение. Опреде-
ление блоков описывается ниже.
Об"ект считается "видимым" в блоке или исходном файле, если
известны тип и имя об"екта в блоке или исходном файле. Об"ект мо-
жет быть "глобально видимым", когда имеется в виду, что он видим
или может быть об"явлен видимым на протяжении всего исходного
файла, образующего программу. Видимость между исходными файлами
рассматривается в разделе 3.5 "Классы памяти".
Блок- это составной оператор. Составные операторы состаят
из об"явлений и операторов, как это описано в разделе 6.2 "Сос-
тавной оператор". Блоки могут быть вложенными.
Структура функции представляет собой совокупность составных
операторов. Таким образом, функции имеют блочную структуру, блоки,
в свою очередь, могут содержать внутри себя другие блоки.
Об"явления и определения внутри блоков находятся на "внут-
реннем уровне". Об"явления и определения вне блоков находятся на
"внешнем уровне". Переменные и функции могут быть об"явлены как
на внешнем уровне, так и на внутреннем. Переменные также могут
быть определены на внутреннем и на внешнем уровне, а функции оп-
ределяются только на внешнем уровне.
Все функции имеют глобальное время жизни, невзирая на то,
где они об"явлены. Переменные, об"явленные на внешнем уровне,
всегда имеют глобальное время жизни. Переменные, об"явленные на
внутреннем уровне, всегда имеют локальное время жизни, однако,
классы памяти, специфицированные как static и extern, могут быть
использованы для об"явления глобальных переменных или ссылок на
них внутри блока.
- 26 -
Переменные, об"явленные или определенные на внешнем уровне,
видимы из точки, в которой они об"являлись или определялись, до
конца файла. Эти переменные можно сделать видимыми в других ис-
ходных файлах путем их об"явления, как описано в разделе 3.5
"Классы памяти".
Однако,переменные, заданные классом памяти "static" на
внешнем уровне, видимы только внутри исходного файла, в котором
они определены.
В общем случае, переменные, об"явленные или определенные на
внутреннем уровне, видимы от точки, в которой они об"явлены или
определены, до конца блока, в котором представлены об"явления или
определения. Эти переменные называются локальными. Если перемен-
ная, об"явленная внутри блока, имеет то же самое имя, как и пере-
менная, об'явленная на внешнем уровне, то определение переменной
в блоке заменяет (имеет предпочтение) определение внешнего уровня
на протяжении блока. Видимость переменной внешнего уровня восста-
навливается при завершении блока.
Блочная видимость может вкладываться. Это значит, что блок,
вложенный внутрь другого блока, может содержать об"явления, кото-
рые переопределяют переменные, об"явленные во внешнем блоке. Пе-
реопределение переменной имеет силу во внутреннем блоке, но пер-
воначальное определение восстанавливается, когда управление возв-
ращается внешнему блоку. Переменные из внешних блоков видимы
внутри всех внутренних блоков до тех пор, пока они не переопреде-
лены во внутреннем блоке.
Функции класса памяти static видимы только в исходном фай-
ле, в котором они определены. Все другие функции являются гло-
бально видимыми.
В табл. 3.1 подытожены основные факторы, которые определяют
время жизни и видимость функций и переменных. В таблице, однако,
не рассматриваются абсолютно все случаи. Более подробная информа-
ция дана в разделе 4.5 "Классы памяти".
------------------------------------------------------------
Уровень Об"ект Спецификатор Время Видимость
класса памяти жизни
------------------------------------------------------------
Внешний Переменная static Глобальное Ограничивается
об"явление отдельным ис-
ходным файлом.
Переменная extern Глобальное Остаток исход-
об"явление ного файла.
Функция об"- static Глобальное Ограничивается
явление или отдельным ис-
определение ходным файлом.
Функция об"- extern Глобальное Остаток исход-
явление или ного файла.
определение
Внутрен- Переменная extern Глобальное Блок
- 27 -
ний об"явление или
или опреде- static
ление
Переменная auto Локальное Блок
об"явление или
или опреде- register
ление
-------------------------------------------------------------
Табл. 3.1. Время жизни и видимость
В следующем примере программы иллюстрируются блоки, вложе-
ния и видимость переменных.
/* i defined at external level */
int i = 1;
/* main function defined at external level */
main ()
{
/* prints 1 (value of external level i) */
printf("%d\n", i);
/* first nested block */
{
/* i and j defined at internal level */
int i = 2, j = 3;
/* prints 2, 3 */
printf("%d\n%d\n", i, j);
/* second nested block */
{
/* i is redefined */
int i = 0;
/* prints 0, 3 */
printf("%d\n%d\n", i, j);
/* end of second nested block */
}
/* prints 2 (outer definition restored) */
printf("%d\n", i);
/* end of first nested block */
}
/* prints 1 (external level definition restored */
printf("%d\n", i);
}
- 28 -
В этом примере показано 4 уровня видимости: самый внешний
уровень и три уровня, образованных блоками. Предполагается, что
функция printf определена где- нибудь в другом месте программы.
Функция main печатает значения 1, 2, 3, 0, 3, 2, 1.
3.5. Классы имен
В любой программе на Си имена используются для ссылок на
различного рода об"екты. Когда пишется программа на Си, то в ней
используются идентификаторы для именования функций, переменных,
формальных параметров, областей памяти и других об"ектов програм-
мы. По определенным правилам в Си допускаются использование одно-
го и того же идентификатора для более чем одного прогаммного
об"екта.
Чтобы различать идентификаторы для различного рода об"ек-
тов, компилятор устанавливает так называемые "Классы имен". Для
избежания противоречий, имена внутри одного класса должны быть
уникальными, однако в различных классах могут появляться идентич-
ные имена. Это означает, что можно использовать один и тот же
идентификатор для двух или более различных об"ектов, если об"екты
принадлежат к различным классам имен. Однозначное разрешение ссы-
лок компилятор осуществляет по контексту данного идентификатора в
программе. Ниже описываются виды об"ектов, которые можно имено-
вать в программе на Си, а также правила их именования.
------------------------------------------------------------
Об"екты Класс имен
-------------------------------------------------------------
Переменные и функции Имена переменных и функций относят-
ся к одному классу имен вместе с
формальными параметрами и перечис-
лимыми константами. Поэтому, имена
переменных и функций этого класса
должны быть отличны от других имен с
той же видимостью.
Однако, имена переменных могут быть
переопределены внутри программных
блоков, как это описано в разделе
3.4 "Время жизни и видимость". Имена
функций также могу быть переопреде-
лены в соответствии с правилами ви-
димости.
Формальные параметры Имена формальных параметров функции
появляются вместе с именами пере-
менных функции, поэтому имена фор-
мальных параметров должны быть от-
личны от имен переменных. Переоб"-
явление формальных параметров внутри
функции приводит к ошибке.
Перечислимые константы Перечислимые константы относятся к
тому же классу имен, что и имена
- 29 -
переменных и функций. Это означа-
ет, что имена перечислимых конс-
тант должны быть отличны от имен
всех переменных и функций с той же
самой видимостью. Однако, подобно
именам переменных, имена
перечислимых констант имеют
вложенную видимость, а это означает,
что они могут быть переопределены
внутри блоков.
typedef имена Имена typedef относятся к одному
классу имен вместе с именами
переменных и функций. Поэтому они
должны быть отличны от всех имен
переменных и функций с той же самой
видимостью, а также от имен
формальных параметров и перечислимых
констант. Подобно именам переменных,
имена, используемые для typedef
типов могут быть переопределены
внутри блоков программы.
Tags Теги перечислений, структур и
совмещений сгруппированы вместе в
одном классе имен. Каждый тег
перечисления, структуры или
соединения должен быть отличен от
других тегов с той же самой
видимостью. Теги не конфликтуют с лю-
быми другими именами.
Элементы Элементы каждой структуры или
совмещения образуют класс имен,
поэтому имя элемента должно быть
уникальным внутри структуры или
совмещения, но это не означает, что
они должны быть отличными от любого
другого имени в программе, включая
имена элементов других структур и
совмещений.
Метки операторов Метки операторов формируют отдельный
класс имен. Каждая метка оператора
должна быть отлична от всех других
меток операторов в той же самой
функции. Метки операторов могут
совпадать с именами меток других
функций.
______________________________________________________________
Пример:
struct student {
char student[20];
- 30 -
int class;
int id;
} student;
В этом примере имя тега структуры, элемента структуры и пе-
ременной относятся к трем различным классам имен, поэтому не бу-
дет противоречия между тремя об"ектами с именем student. Компиля-
тор определит по контексту программы как интерпретировать каждый
из случаев. Например, когда имя student появится после ключевого
слова struct, это будет означать, что появился tag структуры.
Когда имя student появится после операции выбора элемента (-> или
.), то это означает, что имя ссылается на элемент структуры. В
другом контексте имя student является переменной структурного ти-
па.
4. ОБ"ЯВЛЕНИЯ
В этом разделе описываются форматы и составные части об"яв-
лений переменных, функций и типов. Об"явления Си имеют следующий
синтаксис:
[<sc-specifier>][<type-specifier>]<declarator>[=<initializer>]
[,<declarator>[=<initializer>...],
где:
<sc-specifier>- спецификатор класса памяти;
<type-specifier>- имя определяемого типа;
<declarator>- идентификатор, который может быть модифициро-
ван при об"явлении указателя, массива или функции;
<initializer>- задает значение или последовательность зна-
чений, присваиваемых переменной при об"явлении.
Все переменные Си должны быть явно об"явлены перед их ис-
пользованием. Функции Си могут быть об"явлены явно или неявно в
случае их вызова перед определением.
Язык Си определяет стандартное множество типов данных. К
этому множеству можно добавлять новые типы данных посредством их
об"явлений на типах данных уже определенных.
Об"явление Си требует одного или более деклараторов. Декла-
ратор- это идентификатор, который может быть определен с квадрат-
ными скобками ([]), эвездочкой (*) или круглыми скобками () для
об"явления массива, указателя или функции. Когда об'является
простая переменная (такая как символ, целое или плавающее),
структура или совмещение простых переменных, то декларатор- это
идентификатор.
В Си определено четыре спецификатора класса памяти, а имен-
но: auto, extern, register и static.
Спецификатор класса памяти определяет, каким образом об"яв-
ляемый об"ект запоминается и инициализируется и из каких частей
программы можно ссылаться на него. Расположение об"явления внутри
программы, а также наличие или отсутствие других об"явлений- так-
же важные факторы при определении видимости переменных.
Об"явления функций описаны в разделе 4.4.
- 31 -
4.1. Спецификаторы типов
Язык Си поддерживает определения для множества базовых ти-
пов данных, называемых "основными" типами. Названия этих типов
перечислены в Табл. 4.1.
------------------------------------------------------------
Типы целых Типы плавающих Другие типы
------------------------------------------------------------
signed char float void
signed int double
signed short int
signed long int
unsigned char
unsigned int
unsignet short int
unsigned long int
-----------------------------------------------------------
Табл. 4.1. Основные типы.
Перечислимые типы также рассматриваются как основные типы.
Спецификаторы перечислимых типов рассмотрены в разделе 4.7.1. Ти-
пы signed char, signed int, signed short int и signed long int
вместе с соответствующими двойниками unsigned называются типами
целых.
Спецификаторы типов float и double относятся к типу "плава-
ющих". В об"явлениях переменых и функций можно использовать любые
спецификаторы "целый" и "плавающий".
Тип void может быть использован только для об"явления функ-
ций, которые не возвращают значения. Типы функций рассмотрены в
разделе 4.4.
Можно задать дополнительные спецификаторы типа путем об"яв-
ления typedef, описанного в разделе 4.7.2.
При записи спецификаторов типов допустимы сокращения как
показано в табл. 4.2. В целых типах ключевое слово signed может
быть опущено. Так, если ключевое слово unsigned опускается в за-
писи спецификатора типа, то тип целого будет знаковым, даже если
опущено ключевое слово signed.
В некоторых реализациях могут быть использованы опции ком-
пилятора, позволяющие изменить умолчание для типа char со знако-
вого на беззнаковый. Когда задана такая опция, сокращение char
имеет то же самое значение, что и unsigned char, и следовательно
ключевое слово sidned должно быть записано при об"явлении сим-
вольной величины со знаком.
-----------------------------------------------------------
Спецификатор типа Сокращение
-----------------------------------------------------------
signed char char
signed int signed, int
signed short int short, signed short
signed long int long, signed long
unsigned char -
- 32 -
unsigned int unsigned
unsigned short int unsignet short
unsignet long int unsignet long
float -
long float double
------------------------------------------------------------
Табл. 4.2. Спецификаторы и сокращения
Замечание: в этом руководстве в основном используются сок-
ращенные формы, перечисленные в Табл. 4.2, при этом предполагает-
ся, что char по умолчанию знаковый.
В табл. 4.3 для каждого типа приведены: размер распределяе-
мой памяти и области значений переменных для данного типа. Пос-
кольку тип void не представляет переменных, он не включен в эту
таблицу.
-----------------------------------------------------------
Тип Представление Область значений
в памяти величины
-----------------------------------------------------------
char 1 байт -128 до 127
int зависит от
реализации
short 2 байта -32768 до 32767
long 4 байта -2.147.483.648 до 2.147.483.647
unsigned char 1 байт 0 до 255
unsigned зависит от
реализации
unsigned short 2 байта 0 до 65535
unsigned long 4 байта 0 до 4.294.967.295
float 4 байта IEEE стандартное
соглашение
double 8 байт IEEE стандартное
соглашение
------------------------------------------------------------
Табл 4.3 Размер памяти и область значений типов
Тип char используется для запоминания буквы, цифры или сим-
вола из множества представимых символов. Значением об"екта типа
char является ASCII код, соответствующий данному символу. Так как
тип char интерпретируется как однобайтовая целая величина с об-
ластью значений от -128 до 127, то только величины от 0 до 127
- 33 -
имеют символьные эквиваленты. Аналогично, тип unsigned char может
запоминать величины с областью значений от 0 до 255.
Заметим, что представление в памяти и область значений для
типов int и unsigned int не определены в языке Си. По умолчанию
размер int (со знаком и без знака) соответствует реальному разме-
ру целого на данной машине. Например, на 16-ти разрядной машине
тип int всегда 16 разрядов или 2 байта. На 32-ух разрядной машине
тип int всегда 32 разряда или 4 байта. Таким образом, тип int эк-
вивалентен типам short int или long int в зависимости от реализа-
ции.
Аналогично, тип unsigned int эквивалентен типам unsigned
short или unsigned long. Спецификаторы типов int и unsigned int
широко используются в программах на Си, поскольку они позволяют
наиболее эффективно манипулировать целыми величинами на данной
машине.
Однако, размер типов int и unsigned int переменный, поэтому
программы, зависящие от специфики размера int и unsigned int мо-
гут быть непереносимы. Переносимость кода можно улучшить путем
включения выражений с sizeof операцией.
4.2 Область значений величин
Область значений величин- это интервал целых величин от ми-
нимального до максимального, который может быть представлен в за-
данном числе бит. Тем не менее не всегда возможно (см. раздел 4.
"Выражения и присваивания") использовать максимальное или мини-
мальное значение для константы данного типа в выражении.
Например, константное выражение -32768 состоит из арифмети-
ческого отрицания (-), предшествующего величине константы 32768.
Так как 32768- это слишком большое значение для типа short, то
оно задается типом long и следовательно константное выражение
-32768 будет иметь тип long. Величина -32768 может быть представ-
лена как short только путем преобразования ее типа к типу short.
Информация не будет потеряна при преобразовании типа, поскольку
-32768 может быть представлено двумя байтами памяти. Аналогично
такая величина, как 65000 может быть представлена как unsigned
short только путем преобразования ее к типу unsigned short или
заданием величины в восьмиричном или шестнадцатиричном представ-
лении. Величина 65000 в десятичном представлении рассматривается
как знаковая и задается типом long, так как 65000 не соответству-
ет типу short. Эта величина типа long может быть преобразована к
типу unsigned short без потери информации, так как 65000 можно
разместить в двух байтах памяти.
Восьмеричные и шестнадцатиричные константы могут быть зна-
кового и беззнакового типа в зависимости от их размера. Однако,
метод, используемый для назначения типов этим константам гаранти-
рует, что они всегда будут преобразованы к беззнаковому целому.
Числа с плавающей точкой используют IEEE (институт инженеров
электриков и электронщиков) формат. Величины типа float занимают
4 байта, состоящих из бита знака и 7-ми битовой избыточной экспо-
ненты и 24- ех битовой мантиссы. Мантисса представляет число меж-
ду 1.0 и 2.0. Так как старший бит мантиссы всегда 1, он не запо-
минается в памяти. Это представление дает область значений приб-
лизительно от 3.4E-38 до 3.4E38.
- 34 -
Величины типа double занимают 8 байт. Их формат аналогичен
float формату, за исключением того, что порядок занимает 11 бит,
а мантисса 52 бита плюс бит, который опущен. Это дает область
значений приблизительно от 1.7E-308 до 1.7E+308.
4.3. Деклараторы
Синтаксис:
<identifier>
<declarator>[]
<declarator>[constant-expression>]
*<declarator>
<declarator>()
<declarator>(<arg-type-list>)
(<declarator>)
Си позволяет об"являть: массивы величин, указатели на вели-
чины, величины возвратов функций. Чтобы об"явить эти об"екты,
нужно использовать декларатор, возможно модифицированный квадрат-
ными скобками ([]), круглыми скобками () и звездочкой (*), что
соответствует типам массива, функции или указателя. Деклараторы
появляются в об"явлениях указателей, массивов и функций.
4.3.1. Деклараторы массивов, функций и указателей
Когда декларатор состоит из немодифицируемого идентификато-
ра, то об'ект, который об"является, имеет немодифицированный тип.
Звездочка, которая может появиться слева от идентификатора, моди-
фицирует его в тип указателя. Если за идентификатором следуют
квадратные скобки ([]), то тип модифицируется на тип массива. Ес-
ли за идентификатором следуют круглые скобки, то тип модифициру-
ется на тип функции. Сам по себе декларатор не образует полного
об"явления. Для этого в об"явление должен быть включен специфика-
тор типа. Спецификатор типа задает тип элементов массива или тип
адресуемых об"ектов и возвратов функции.
Следующие примеры иллюстрируют простейшие формы декларато-
ров:
1. int list[20]
2. char *cp
3. double func(void),
где:
1. Массив list целых величин
2. Указатель cp на величину типа char
3. Функция func без аргументов, возвращающая величину
double
- 35 -
4.3.2. Составные деклараторы
Любой декларатор может быть заключен в круглые скобки.
Обычно, круглые скобки используются для спецификации особенностей
интерпретации составного декларатора. Составной декларатор- это
идентификатор, определяемый более чем одним модификатором масси-
ва, указателя или функции.
С отдельным идентификатором могут появиться различные ком-
бинации модификаторов массива, указателя или функции. Некоторые
комбинации недопустимы. Например, массив не может быть композици-
ей функций, а функция не может возвратить массив или функцию. При
интерпретации составных деклараторов квадратные и круглые скобки
(справа от идентификатора) имеют приоритет перед звездочкой (сле-
ва от идентификатора). Квадратные или круглые скобки имеют один и
тот же приоритет и рассматриваются слева направо. Спецификатор
типа рассматривается на последнем шаге, когда декларатор уже пол-
ностью проинтерпретирован. Можно использовать круглые скобки,
чтобы изменить порядок интерпретации на необходимый в данном слу-
чае.
При интерпретации составных деклараторов может быть предло-
жено простое правило, которое читается следующим образом: "изнут-
ри- наружу". Нужно начать с идентификатора и посмотреть вправо,
есть ли квадратные или круглые скобки. Если они есть, то проин-
терпретировать эту часть декларатора, затем посмотреть налево,
если ли звездочка. Если на любой стадии справа встретится закры-
вающая круглая скобка, то вначале необходимо применить все эти
правила внутри круглых скобок, а затем продолжить интерпретацию.
на последнем шаге интерпретируется спецификатор типа. В следующем
примере проиллюстрированы эти правила. Последовательность шагов
при интерпретации перенумерована.
char *(*(*var) ()) [10];
^ ^ ^ ^ ^ ^ ^
7 6 4 2 1 3 5
1. Идентификатор var об'явлен как
2. Указатель на
3. Функцию, возвращающую
4. Указатель на
5. Массив из 10 элементов, который состоит
6. Из указателей на
7. Величины типа char.
В следующих примерах показывается каким образом круглые
скобки могут поменять смысл об"явлений.
1. int *var[5]; - массив указателей на величины типа int.
- 36 -
2. int (*var)[5]; - указатель на массив величин типа int.
3. long *var(long,long); - функция, возвращающая указатель
на величину типа long.
4. long (*var) (long,long); - указатель на функцию, возвра-
щающую величину типа long.
5. struct both {
int a;
char b;
} ( *var[5] ) ( struct both, struct both);
массив указателей на функции, возвращающих структуры.
6. double ( *var( double (*) [3] ) ) [3];
функция, возвращающая указатель на массив из трех величин
типа double.
7. union sign {
int x;
unsigned y;
} **var[5] [5];
массив массивов указателей на указатели совмещений.
8. union sign *(*var[5]) [5];
массив указателей на массив указателей на совмещения.
Описание примеров:
В первом примере, модификатор массива имеет высший приори-
тет, чем модификатор указателя, так что var об"является массивом.
Модификатор указателя определяет тип элементов массива; элемента-
ми являются указатели на величины типа int.
Во втором примере скобки меняют значение об"явления первого
примера. Теперь модификатор указателя имеет более высокий приори-
тет, чем модификатор массива, и переменная var об"является как
указатель на массив из пяти величин типа int.
В третьем примере модификатор функции имеет более высокий
приоритет, чем модификатор указателя, так что переменная var
об"является функцией, возвращающей указатель на величину типа
long. Функция об"явлена с двумя аргументами типа long.
Четвертый пример похож на второй. Скобки задают более высо-
кий приоритет модификатору указателя, и поэтому переменная var
об"является как указатель на функцию, возвращающую величину типа
long. По прежнему функция об"явлена с двумя аргументами типа
long.
Элементы массива не могут быть функциями. Взамен этому в
пятом примере показано, как об"явить массив указателей на функ-
ции. В этом примере переменная var об"явлена как массив из пяти
указателей на функции, возвращающие структуры с двумя элементами.
Оба аргумента функции об"явлены как структуры типа both. Заметим,
что круглые скобки, в которые заключено выражение *var[5], обяза-
тельны. Без них об"явление будет неверным, поскольку будет об"яв-
лен массив функций:
- 37 -
/* ILLEGAL */
struct both *var[5] ( struct both, struct both );
В шестом примере показано, как об"являть функцию, возвраща-
ющую указатель на массив. Здесь var об"явлена функцией, возвраща-
ющей указатель на массив из трех величин типа double. Тип аргу-
мента функции задан составным абстрактным декларатором. Круглые
скобки, заключающие звездочку, требуются, так как в противном
случае типом аргумента был бы массив из трех указателей на вели-
чины типа double.
В седьмом примере показано, что указатель может указывать
на другой указатель и массив может состоять из массивов. Здесь
var- это массив из пяти элементов. Каждый элемент, в свою оче-
редь, так же массив из пяти элементов, каждый из которых является
указателем на указатель совмещения, состоящего из двух элементов.
В восьмом примере показано, как круглые скобки изменили
смысл об"явления. В этом примере var- это массив из пяти указате-
лей на массив из пяти указателей на совмещения.
4.3.3. Деклараторы со специальными ключевыми словами
В MSC можно использовать следующие специальные ключевые
слова:
cdecl
far
fortran
huge
near
pascal
Эти ключевые слова используются для изменения смысла об"яв-
лений переменной и функции. Более полная информация о применении
специальных ключей дана в системной документации по пакету MSC.
Когда специальное ключевое слово встречается в деклараторе,
то оно модифицирует об"ект, расположенный справа от ключевого
слова. Допускается более одного ключевого слова, модифицирующего
об"ект. Например, идентификатор функции может быть модифицирован
двумя ключевыми словами far и pascal. Порядок ключевых слов в
этом случае не важен, то есть far pascal и pascal far имеют один
и тот же смысл. В различных частях об"явления могут быть исполь-
зованы два или более ключевых слов для модификации смысла состав-
ляющих частей об"явления.
Например, в следующем об"явлении содержится в различных
местах два ключевых слова far и ключевое слово pascal:
int far * pascal far func(void);
Идентификатор функции func модифицируется ключевыми словами
pascal и far. func возвращает величину, об"явленную как far- ука-
- 38 -
затель на величину типа int.
Как и прежде в любом об"явлении могут быть использованы
круглые скобки для изменения порядка интерпретации. Правила ин-
терпретации составных об"явлений со специальными ключевыми слова-
ми остаются теми же, что и без них. В следующих примерах показано
использование специальных ключевых слов в об"явлениях.
\*************** Example 1 ********************\
int huge database[65000];
\*************** Example 2 *********************\
char * far * x;
\*************** Example 3 *********************\
double near cdecl calc(double,double);
double cdecl near calc(double,double);
\*************** Example 4 *********************\
char far fortran initlist[INITSIZE];
char far *nextchar, far *prevchar, far *currentchar;
\*************** Example 5 **********************\
char far *(far *getint) (int far *);
^ ^ ^ ^ ^ ^
6 5 2 1 3 4
В первом примере об"является массив huge, поименованный
database, содержащий 65000 элементов типа int. Декларатор массива
модифицируется ключевым словом huge.
Во втором примере ключевое слово far модифицирует располо-
женную справа звездочку, делая x far- указателем на указатель к
величине типа char. Это об"явление эквивалентно и такой записи:
char * (far *x);
В примере 3 показано два эквивалентных об"явления. Оба
об"являют calc как функции с near и cdecl атрибутами.
В примере 4 также представлено два об"явления: первое
об"являет far fortan- массив символов, поименованный initlist, а
второе об"являет три far- указателя, поименованные nexchar,
prevchar и currentchar. Указатели могут быть использованы для за-
поминания адресов символов массива initlist. Заметим, что ключе-
вое слово far должно быть повторено перед каждым декларатором.
В примере 5 показано более сложное об"явление с различными
случаями расположения ключевого слова far. Интерпретация этого
об"явления состоит из следующих шагов:
1. Идентификатор getint об"является как
2. far- указатель на
- 39 -
3. Функцию, требующую
4. Один аргумент, который является far- указателем на вели-
чину типа int,
5. И возвращающую far- указатель на
6. Величину типа char
Заметим, что ключевое слово far всегда модифицирует об"ект,
который следует справа.
4.4. Об"явления переменной
В этом разделе дано описание синтаксиса и семантики об"яв-
лений переменной. В частности, здесь об"ясняется каким образом
об"явить следующие переменные:
Тип переменной Описание
Простая переменная Переменная целого или плаваю-
щего типа.
Переменная перечис- Простая переменная целого типа
ления. которая принимает значения из
предопределенного набора зна-
чений поименованных констант.
Структура Переменная, которой соответс-
твует композиция отдельных пе-
ременных, типы которых могут
отличаться.
Совмещение Переменная, которой соответс-
твует композиция отдельных пе-
ременных, занимающих одно и то
же пространство памяти. Типы
переменных композиции могут
отличаться.
Массив Переменная, представляющая на-
бор элементов одного типа.
Указатель Переменная, которая указывает
на другую переменную (содержит
местоположение другой перемен-
ной в форме адреса).
Общий синтаксис об"явлений переменных следующий:
[<sc-spesifier>] <type-spesifier> <declarator> [,<declarator>...],
где <type- spesifier> - задает тип данных, представляемых
переменной, а <declarator> - это имя переменной, возможно модифи-
цированное для об"явления массива или указателя. В об"явлении мо-
Документ
Категория
Рефераты
Просмотров
20
Размер файла
55 Кб
Теги
1/--страниц
Пожаловаться на содержимое документа