static int n; |
|
|
|
int main(void) |
|
|
|
{ |
|
|
|
double x, y; |
|
|
|
/* |
Переменные m и n доступны |
*/ |
|
/* |
|
*/ |
|
return 0; |
|
|
|
} |
|
|
|
/* |
Файл f2.c |
*/ |
|
extern int m; /* Ссылочное объявление переменной |
m*/ |
||
|
/* Переменная m доступна |
*/ |
|
Вэтом программном коде имеются пять объявлений. Четыре из них находятся в файле f1.c. Все эти объявления относятся к категории определяющих объявлений. С помощью этих объявлений устанавливаются типы, и выделяется память для четырех переменных: m, n, x и y. В начале определены две целочисленные переменные m и n. Обе переменные являются внешними. Их область действия начинается от точки определения и распространяется до конца файла f1.c. В связи с этим переменные m и n доступны в теле функции main(). Переменная m имеет внешнее связывание. Это означает, что ее можно сделать доступной в любом другом файле программы. Например, она будет доступна в файле f2.c. Доступной ее в файле f2.c делает наличие в нем ссылочного объявления этой переменной. Время жизни этих переменных совпадает со временем выполнения программы.
Переменная n имеет внутреннее связывание. Это обусловлено наличием
вее объявлении зарезервированного слова static. В файле f2.c эта переменная не доступна.
Втеле функции main() объявлены две локальные переменные x и y, имеющие тип double. Область действия этих переменных начинается от точки объявления и заканчивается закрывающей скобкой тела функции main(). В связи с этим рассматриваемые переменные нельзя использовать в любой другой функции программы, кроме функции main(). Время жизни этих переменных заканчивается в момент завершения работы вызова функции main().
Рассмотрим теперь более детально вопрос об инициализации переменных во время их объявления. Следует отметить, что эта операция должна для каждой переменной выполняться индивидуально. Для инициализации некоторой переменной необходимо записать оператор присваивания “=”, затем записывается инициализатор. Следует отметить, что для внешних переменных в качестве инициализатора можно использовать только так называемые константные выражения, а при инициализации локальных переменных допустимо использовать выражения общего вида. Приведем пример.
Пример 2.
21
int m = 10; int main(void)
{
double x = 1; double y = sin(x); /* ... */ return 0;
}
В этом примере инициализированы во время объявления три переменные: внешняя переменная m и две локальные переменные x и y.
Наконец приведем пример объявления константной переменной. Как указывалось ранее, в объявлении такой переменной следует использовать зарезервированное слово const.
Пример 3.
const int MaxSize = 100; int main(void)
{
MaxSize = 200; /* Ошибка. Значение MaxSize изменять нельзя */
/* */
}
В этом примере объявлена неизменяемая переменная MaxSize. Значение этой переменной зафиксировано в момент ее объявления и не может быть изменено в процессе выполнения программы.
Символическая константа – это константа, которой в программе назначено некоторое имя. Применение символических констант вместо явных констант повышает читабельность программы и облегчает внесение в нее изменений. Одно из правил хорошего стиля программирования требует, чтобы в программе не было так называемых “магических чисел”. Под магическими числами понимаются конкретные числовые значения. Сложность работы с “магическими числами” заключается в отсутствии у них семантики. Символическая константа наделяет конкретное значение определенной семантикой, что и способствует повышению читабельности программы. В языке Си имеются три способа представления символических констант, в которых предусматривается использование соответственно:
●директивы препроцессора define.
●зарезервированного слова const.
●констант перечисления.
Приведем пример объявления символических констант.
#define PI 3.14159
const int BirthDay = 1964;
enum {MaxSize = 100};
Более предпочтительным способом определения символических констант является способ, основанный на применении зарезервированного
22
слова const. Это обусловлено тем обстоятельством, что при использовании этого способа работает аппарат контроля типов.
Имеется один недостаток, связанный с использование определения символических констант с помощью зарезервированного слова const. Дело в том, что символическую константу, определенную таким образом нельзя использовать при определении размера массивов. В этом случае можно воспользоваться константами перечисления (массивы будут рассматриваться во второй части пособия).
При программировании на языке Си необходимо различать следующие программные элементы:
●операторы,
●выражения,
●инструкции.
Операторы (operators) предназначены для выполнения некоторых элементарных действий. Например, в языке Си имеется оператор сложения ( + ), предназначенный для сложения двух чисел.
Выражение (expression) – конструкция языка, используемая для вычисления одного значения или (и) достижения побочного эффекта (side effect). О побочных эффектах при вычислении выражений см. п. 1.14.3. Выражения строятся из операндов, операторов и круглых скобок. В качестве операндов могут выступать константы, переменные, вызовы функций и выражения в круглых скобках. Литерал и переменную можно рассматривать как частный случай выражения. Пусть, например, имеется следующая синтаксическая конструкция:
3 + 5
Это пример простейшего выражения, в котором используется упоминавшийся выше оператор сложения.
Инструкция (statement) – это отдельное предложение языка Си, предписывающее компилятору выполнить некоторые действия. К числу инструкций относятся такие управляющие конструкции, как if, for и т. д. Важно отметить, что в языке Си имеется тесная связь между выражениями и инструкциями. Дело в том, что любое выражение, после которого поставлена точка с запятой, становится инструкцией. Такой вид инструкции называется инструкцией – выражением. Приведем пример. Пусть a, b и y – переменные типа double. Тогда следующая синтаксическая конструкция является выражением:
y = a * b
В этом выражении используются два оператора. Первым из них является оператор присваивания (=), а вторым - оператор умножения (*). Если теперь в конце этого выражения поставить точку с запятой, то получим инструкцию присваивания:
y = a * b;
23
В настоящем разделе основное внимание будет уделено операторам и выражениям.
Операторы языка Си делятся на три категории:
●унарные,
●бинарные,
●тернарные.
Воснову этой классификации положено количество операндов, с которыми работает оператор. Унарные операторы имеют только один операнд, бинарные операторы работают с двумя операндами и у единственного в языке Си тернарного оператора – три операнда (? :). Например:
●-a (выражение содержит унарный минус),
●a – b (выражение содержит бинарный минус),
●a > b ? a : b (выражение содержит тернарный оператор).
Важнейшими характеристиками операторов являются их приоритет и ассоциативность. Использование этих характеристик позволяет определить смысл выражения. Если отсутствуют скобки, то операнды сильнее связываются с операторами, имеющими более высокий приоритет. Можно говорить о том, что операторы с более высоким уровнем приоритета сильнее “притягивают” операнды. Если два оператора имеют одинаковый приоритет, то связывание определяется ассоциативностью. Имеются две разновидности ассоциативности операторов: левосторонняя и правосторонняя. При левосторонней ассоциативности процесс связывания начинается слева, а при правосторонней ассоциативности – справа. Приоритет и ассоциативность позволяют выполнить группировку операторов и операндов в выражении, содержащем более одного оператора.
Приоритеты операторов языка Си и их ассоциативность представлены в таблице, приведенной ниже.
Приоритет |
Название оператора |
Знак |
Ассоциативность |
|
|
операции |
|
|
Функция |
|
|
|
() |
|
|
|
Индексирование |
[] |
|
1 |
Селектор поля структуры |
-> |
Левосторонняя |
(высший) |
Селектор поля структуры |
. |
|
|
Инкремент постфиксный |
++ |
|
|
Декремент постфиксный |
-- |
|
2 |
Логическое отрицание |
! |
Правосторонняя |
|
Побитовое отрицание |
~ |
|
|
Инкремент префиксный |
++ |
|
24
3
4
5
6
7
8
9
10
11
12
13
14
15
(низший)
Декремент префиксный Унарное сложение Унарное вычитание Разыменование Взятие адреса Приведение типа Размер объекта Умножение Деление
Вычисление остатка деления Сложение бинарное
Вычитание бинарное Сдвиг влево Сдвиг вправо Меньше Неменьше Больше Небольше
Сравнение на равенство Сравнение на неравенство Побитовое И Побитовое исключающее ИЛИ Побитовое ИЛИ Логическое И
Логическое ИЛИ Вычисление выражения по условию
Присваивания:
Запятая
--
+
-
*
&
(тип) sizeof
*
/
%
+
-
<<
>>
<
>=
>
<=
==
!=
&
^
|
&&
||
?:
= += -= *= /= %= >>= <<= &= ^= &= ^= |=
,
Левосторонняя
Правосторонняя
Левосторонняя
В таблице операторы, имеющие одинаковый приоритет, объединены в группы. За каждой группой операторов закреплен номер, определяющий ее приоритет. Наибольший приоритет (1) имеет группа, расположенная вначале таблицы, а наименьший (15) – имеет оператор “запятая”, который находится в конце таблицы.
Приведем примеры выражений, при вычислении которых необходимо учитывать приоритет и ассоциативность операторов.
Пример 1. Рассмотрим следующее выражение:
25