Общий формат объявления переменных:
[<спецификатор_класса_памяти>] [<модификатор>] <тип> <имя1>[=<инициализатор1>], <имя2>[=<инициализатор2>],…, <имяN>[=<инициализаторN>];
Необязательный элемент <спецификатор_класса_памяти> (необязательный элемент далее в записи форматов будет помещаться в квадратные скобки) одно из ключевых слов: auto, register, static, extern.
Необязательные элемент <модификатор> - могут быть ключевые слова: const, volatile и др.
Модификатор const означает, что инициализация (присвоение начального значения) переменной обязательна, и далее значение переменной изменять нельзя. Модификатор volatile означает, что данная переменная может быть изменена где- то в другом месте, а не обязательно в этой программе, например, программой обработки прерываний (модификатор влияет на режим оптимизацию работы компилятора).
Примеры:
int i, j=10;
const float pi=3.14; // Далее запрещено изменять значение pi
extern double x; // Это есть описание, определение переменной в другом
// месте.
unsigned char C1=‘A’, C2=10, C3;
Константы в языке Си
Кроме переменных в программе часто используются константы.
Константа – это значение, которое не может быть изменено в процессе работы программы.
В языке Си выделяют следующие разновидности констант, которые могут иметь разные типы и формы представления:
- Целые константы.
Целые константы существуют для представления в программе целых значений. Константы могут иметь разные типы. Тип константы определяет представление константы в оперативной памяти: кодировку константы, объем в байтах, какой набор значений может представляться константой, а также операции, которые можно выполнять с константой. Существуют следующие типы целых констант:
- тип int (по умолчанию), в оперативной памяти константа данного типа кодируется в MS DOS – 2 байтами, в Win32 – 4 байтами, примеры: 1245, 6, 175, 5, 1425;
- тип long – в оперативной памяти константа кодируется 4 байтами, константа будет иметь этот тип, если диапазон выходит за тип int (в MS DOS), или явно указывается тип с помощью суффикса l (L), примеры: 12l, 14567L, 125234 (для MS DOS);
- константы могут быть представлены типами unsigned int и unsigned long, для явного указания необходимо использовать суффикс u (U), примеры 105u (unsigned int), 105ul(unsigned long).
Целые константы всегда задают неотрицательные значения, для задания отрицательного значения используют операцию унарный минус, которую применяют к целой константе: -1000, -200l.
Целые константы также могут иметь различные формы представления. Формы представления введены для удобства программиста, одно и то же значение константы может быть представлено в различных формах в исходном тексте программы, при этом после компиляции полученный исполняемый код и кодирование константы в памяти будет идентично и не зависит от формы представления. Для целых констант существуют три формы представления:
- в десятичной системе счисления (по умолчанию) – 1234, 378l, 346;
- в шестнадцатеричной системе счисления, признаком данного представления являются 2 первых символа константы 0x (или 0X), в константе можно использовать шестнадцатеричные цифры: 0..9, A (a), B (b), …, F (f), примеры – 0x10, 0x10acd, 0XFFFF;
- в восьмеричной системе счисления, признаком данного представления является первый символ 0, в константе можно использовать восьмеричные цифры: 0..7, примеры – 010, 070, 01237;
- Вещественные константы.
Вещественные константы служат для представления вещественных значений, которые могут иметь целую и дробные части. По аналогии с целыми константами существуют различные типы и формы представления вещественных констант. Для вещественной константы можно применять операцию унарный минус с целью получения отрицательного значения.
Существует следующие три типа вещественных констант:
- тип double (по умолчанию), в оперативной памяти константа кодируется 8 байтами, примеры: 12.5,.123, 0.5, 1. (наличие точки в таком представлении обязательно);
- тип float – 4 байта, используется суффикс f (F), примеры: 10.5f, 0.123F;
- тип long double – 10 байт, используется суффикс l (L), примеры: 10.5l, 0.9L.
Существуют две формы представления вещественных констант:
- форма с точкой (десятичная форма), примеры: 10.125, 1. (значение 1.0), .125 (значение 0.125), 0.125, представляется в форме десятичной дроби, состоит из трех основных элементов целой части, точки, дробной части, причем, целая или дробная часть могут отсутствовать, наличие точки обязательно, если целая часть отсутствует, то она считается равной 0, если дробная часть отсутствует, то она также считается равной 0;
- форма со знаком экспоненты (экспоненциальная форма): 1e-5, 12.23E4F (тип константы float), представляется в виде мантиссы и порядка, мантисса записывается слева от знака экспоненты (Е или е), порядок — справа от знака. Значение константы определяется как произведение мантиссы и возведенного в указанную в порядке степень числа 10..
- Символьные константы. Изначально в Си стандарта ANSI символьные константы представлялись одним символом, который мог быть буквой, цифрой, знаком пунктуации или специальный символом, заключенным в апострофы, примеры, ‘a’, ‘d’, ‘1’, ‘.’, ‘ ‘, данные константы представлены в памяти типом char и занимают 1 байт. Значением символьной константы является числовое значение кода символа в кодировке, используемой в данной операционной системе, например, в кодировке ASCII.
Символьные константы могут участвовать в арифметических операциях на правах полноценных целых чисел, хотя чаще их сравнивают с другими символами. Некоторые символы представляются в символьных и строковых константах с помощью специальных управляющих последовательностей символов, начинающихся с ‘\’ (косая обратная наклонная черта), называемых эскейп- последовательностями. Примеры: ‘\\’ - символ обратный слеш, ‘\’’ - символ апостроф, ‘\”’ - символ двойные кавычки, ‘\n’ – символ конец строки (код 10), ‘\r’ – возврат каретки (код 13), ‘\a’ – звуковой сигнал (код 7), ‘\b’ – возврат на шаг (забой) (код 8). Кроме того, любой символ можно представить в виде его кода в восьмеричной или шестнадцатеричной системах счисления в форматах ‘\ooo’ или ‘\xhh’, где o – восьмеричная цифра, а h – шестнадцатеричная цифра (значение восьмеричного кода не может превышать 255). Например, символ пробел с десятичным кодом 32 может быть записан как ‘ ‘, или в шестнадцатеричном коде как ‘\x20’, или в восьмеричном коде как ‘\40’.
В некоторых компиляторах, например, Visual C++ разрешены много символьные константы (до 4-х символов): ‘asdf’, ‘GR’, они представлены в памяти типом int (первый символ – младший байт).
- Строковые константы – последовательность символов, заключенных в кавычки (не в апострофы), внутри строковых констант допускается использовать эскейп- последовательности или коды символов. Пример: “начало строки\nтекст с новой строки”, при печати текст будет выводиться на две строки. Формально строковая константа является массивом символов. Во внутреннем представлении строки в оперативной памяти в конце присутствует нулевой символ ‘\0’, так что физический объем памяти для хранения строки превышает количество символов, записанных между кавычками на единицу.
- Константы типа перечислений. Можно создавать перечисляемый тип, содержащий константы. Переменным этого типа можно присваивать значения только этих констант. Данные константы представляются в памяти точно так же, как константы типа int.
Формат перечислений следующий:
enum <имя_типа> {
<имя1>[=<инициализатор1>],
<имя2>[=<инициализатор2>],
……………….
<имяN>[=<инициализаторN>] };
Идентификаторы <имя1>, <имя2>, …, <имяN> -выступают далее в качестве констант, по умолчанию, если нет инициализатора, первая константа инициализируется 0, каждая последующая на 1 больше.
Пример перечисления
enum A
{
a, // 0
b, // 1
c=10, // 10
d // 11
};
Далее в программе обращаться к данным константам, int i=b; переменной i будет присвоено значение 1.
- Существует еще одна константа – это, так называемый, нулевой указатель, для задания которого введена именованная константа NULL, используется для задания значения указателя, который ни на что не указывает. Обычно соответствует значению 0, но не обязательно во всех системах.
1.2.5. Операторы – выражения и операции языка Си
Для вычисления значений используются, так называемые, операторы- выражения.
Операторы – выражения, строятся из операндов и знаков операций, выражение задает правило вычисления некоторого значения, проведем следующую классификацию выражений:
• на базе операции присваивания «=»: x=y+10;
• на базе операций инкремент, декремент (++ --): i++; --j;
• вызов функции: f1();
• комбинированные выражения: x=i++ + f();
Под операцией будем понимать некоторое действие, выполняемое над операндами (аргументами операции), результат операции всегда некоторое значение определенного типа, которое может быть использовано справа от операции присваивания (может быть присвоено некоторой переменной).
Операции языка Си делятся на следующие классы:
Унарные операции:
«-» - «унарный минус» применяется к арифметическим операндам (целым, вещественным переменным или константам), результат операции значение операнда с противоположным знаком;
«+» - «унарный плюс» операция ничего не делает, введена для симметрии с операцией «унарный минус»;
«*» - «обращение по адресу», операция применяется к указателям, результат операции значение объекта (операнда), на который указывает указатель;
«&» - «получение адреса», результат операции – адрес объекта (переменной);
«~» - «поразрядное отрицание» операция применяется только к целым операндам, результат операции целое значение, в котором разряды исходного операнда инвертированы;
«!» - «логическое отрицание» («логическое НЕ»), дает в результате значение 0, если операнд есть истина (не нуль), и значение 1, если операнд равен нулю (в Visual C++ тип результата bool), следует отметить, что в базовом Си стандарта ANSI отсутствовал в явном виде логический тип, который бы принимал 2 значения: «истина» и «ложь», вместо логического типа использовался, как правило, целый тип, значение 0- интерпретировалось, как «ложь», любое значение отличное от 0 было- «истина»;
(<тип>) – «операция преобразования типа», была рассмотрена ранее;
«sizeof» – операция определения размера предназначена для вычисления размера объекта или типа в байтах, и имеет две формы:
sizeof выражение или sizeof(выражение)
sizeof(тип)
++ - инкремент (увеличение на 1), -- - декремент (уменьшение на 1), имеют две формы записи — префиксную, когда операция записывается перед операндом, и постфиксную. Если операции используются сами по себе (в операторе только одна операция), то разницы между двумя формами нет. Если операция применяется внутри выражения с другими операциями, то в префиксной форме сначала изменяется операнд, а затем его новое значение подставляется в выражение, а в постфиксной форме в выражение подставляется старое значение, а затем изменяется значение операнда. Например,
int i=10, j;
j=++i; // Префиксная форма операции
В результате выполнения данного фрагмента, i и j будут равны 11 (переменной j присваивается новое значение i, увеличенное на 1), если изменить форму операции ++:
int i=10, j;
j=i++; // Постфиксная форма операции
то, после выполнения i будет равно 11, а j будет равно 10, переменной j присваивается старое значение переменной i, а затем оно увеличивается на 1. Операции чаще применяют к целым операндам, но их можно применять к вещественным операндам и даже к указателям.
Бинарные операции можно разделить на следующие классы:
Арифметические:
«+» - бинарный плюс;
«-» - бинарный минус;
«*» - умножение;
«/» - деление;
% - получение остатка от деления.
Первые четыре операции применяются к арифметическим операндам: целым или вещественным, операции «+» и «-» ограниченным способом могут применяться к указателям. Операция – «%» применяется только к целым операндам.
Логические:
«&&» - логическое И;
«||» - логическое ИЛИ;
«^» - логическое исключающее ИЛИ.
Операнды логических операций могут иметь арифметический тип или быть указателями, при этом операнды в каждой операции могут быть различных типов. Преобразования типов не производятся, каждый операнд оценивается с точки зрения его эквивалентности нулю (операнд, равный нулю, рассматривается как «ложь», не равный нулю — как «истина»). В Visual C++ тип результата int или bool в зависимости от типов операндов.
Поразрядные:
«&» - поразрядное И;
«|» - поразрядное ИЛИ;
«^» - поразрядное исключающее ИЛИ;
«>>» - поразрядный сдвиг вправо;
«<<» - поразрядный сдвиг влево.
Данные операции применяются только к целочисленным операндам и работают с их двоичными представлениями. При выполнении операций «&», «|», «^» операнды сопоставляются побитово (первый бит первого операнда с первым битом второго, второй бит первого операнда со вторым битом второго, и т д.).
Операции сдвига сдвигают двоичное представление первого операнда влево или вправо на количество двоичных разрядов, заданное вторым операндом. При сдвиге влево «<<» освободившиеся разряды обнуляются. При сдвиге вправо «>>» освободившиеся биты заполняются нулями, если первый операнд беззнакового типа, и знаковым разрядом в противном случае.
Отношения (сравнения):
«==» - равно (не путать с операцией присваивания «=»);
«!=» - не равно;
«>» - больше;
«<» - меньше;
«>=» - больше или равно;
«<=» - меньше или равно.
Операции отношения сравнивают первый операнд со вторым. Операнды могут быть арифметического типа или указателями. Результатом операции является значение «истина» (любое значение не равное 0, как правило, 1) или «ложь» (0). В Visual C++ тип результата bool.
Присваивания:
«=» - простое присваивание.
Первый операнд должен быть L -значением, т.е. областью памяти, куда будет помещен результат операции, второй – выражением. Сначала вычисляется выражение, стоящее в правой части операции, а потом его результат записывается в область памяти, указанную в левой части. Так называемое, L -значение (L- value) (леводопустимое значение, может быть использовано слева от операции присваивания), так обозначается любое выражение, адресующее некоторый участок памяти, в который можно занести значение.
op= (где op – символ бинарной операции) – комбинированное присваивание, комбинация бинарное операции с операцией присваивания, например, «+=» - присваивание со сложением, по аналогии существуют операции: «*=», «/=», «-=», «%=», «&=», «|=», «^=» и др. Комбинированные операции работают по следующему правилу:
i+=10; аналогично i=i+10;
Другие бинарные операции, просто их перечислим:
() – вызов функции;
[ ] – обращение к элементу массива;
«.» («точка») – обращение к полю переменной структурного типа;
«->» - обращение к полю переменной структурного типа через указатель;
«,» («запятая») – последовательное вычисление, может ставиться между выражениями, выражения вычисляются последовательно, результат операции результат второго операнда (выражения).
Тернарная операция:
«?:» - условная операция.
Формат: <операнд1>? <операнд2>: <операнд3>
Первый операнд имеет тип, заменяющий логический – арифметический или указатель, если первый операнд имеет значение «истинно», то результат операции – значение второго операнда, а если «ложь», то результат операции значение третьего операнда. Пример,
y= x>=0? x: -x;
переменной y присваивается значения модуля переменной x.
Если в одном выражении присутствуют несколько разных операций, то они выполняются в соответствии с приоритетами – в первую очередь выполняются операции с более высоким приоритетом. Каждая операция в Си имеет свой приоритет, всего существует 15 классов приоритетов. Если в одном выражении присутствует несколько одинаковых операций, то они могут выполняться или слева направо или справа налево, это определяет такое свойство операций, которое называется ассоциативностью (порядок выполнения операции в выражении). Приоритеты и ассоциативность операций языка Си представлены в таблице 2, операции в таблице представлены в порядке убывания приоритета.
Для изменения порядка выполнения операций используются круглые скобки.
Примеры:
y = a + b * 10; // В первую очередь выполняется *, затем +, далее =
y = (a + b) * 10; // В первую очередь выполняется + (из- за скобок), затем *, далее =
a = b = c = 100; // Операции = выполняются справа налево,
// всем переменным будет присвоено значение 100
Условный оператор
Условный оператор служит для бинарного ветвления фрагмента исходного кода программы, в зависимости от условия («истина» или «ложь») выполняется или один фрагмент кода (оператор) или другой фрагмент кода (оператор).
Таблица 2 – Приоритеты и ассоциативность операций языка Си
Приоритет (Ранг) | Операции | Наименование | Ассоциативность |
() [] ->. | Первичные | ® | |
! ~ + - ++ -- & * (тип) sizeof | Унарные | ||
* / % | Мультипликативные | ® | |
+ - | Аддитивные | ® | |
«» | Поразрядный сдвиг | ® | |
< <= >= > | Отношение (сравнение) | ® | |
==!= | Отношение (сравнение) | ® | |
& | Поразрядное И | ® | |
^ | Поразрядное исключающее ИЛИ | ® | |
| | Поразрядное ИЛИ | ® | |
&& | Логическое И | ® | |
|| | Логическое ИЛИ | ® | |
?: | Условная | ||
= *= /= %= += -= &= ^= |= <<= >>= | Простое и составное присваивание | ||
, (операция "запятая") | Последовательное вычисление | ® |
Формат условного оператора:
if (<выражение>) <оператор1>
[ else <оператор2> ]
Примечание. Далее при записи форматов различных конструкций примем следующие обозначения: оператор или выражение заключаем в скобки <>, точка с запятой («;») после оператора при описании формата не ставится, так как считается, что она входит в состав оператора, или это составной оператор (блок), а элемент, заключенный в квадратные скобки [] (за исключением использования массивов), считается необязательным, т.е. может отсутствовать.
Тип выражения должен быть любым типом, заменяющий логический тип (целым, вещественным, указателем), если значение выражение равно 0, то оно считается ложным, в противном случае, истинным.
Пример:
if (x>=0) y=x; else y=-x; // Переменной y присваивается значение модуля x
Если необходимо объединить несколько операторов, то они объединяются в составной оператор (блок).
Пример:
if (x>0) { y=x; z=x+y; }
else { y=-x; z=x*x+y*y; }
Условный оператор может быть неполным, часть, начинающаяся с else, отсутствует. В этом случае в зависимости от условия или выполняется оператор после if или он не выполняется.
Представляют интерес вложенные условные операторы. Рассмотрим пример:
if (x==1) if (y==1) printf(“x==1 and y==1”);
else printf(“x==1 and y!=1”);
В этом примере ключевое слово else относится ко второму if, первый оператор if является не полным. Следует отметить, что условный оператор if … else … считается одним оператором, он может быть сам внутри условного оператора или быть внутри тела цикла. Правило, позволяющее найти соответствие между if и else в случае вложенных условных операторов, следующее: ищем первое ключевое слово else, оно относится к ближайшему if, которое предшествует ему, ко второму else относится if, которое предшествует отмеченному ранее if и т.д.
Если требуется изменить описанный порядок соответствия if и else при вложенных условных операторах, то с помощью фигурных скобок необходимо организовать составной оператор. В примере:
if (x==1) { if (y==1) printf(“x==1 and y==1”); }
else printf(“x!=1”);
ключевое слово else относится к первому if, второй оператор if является не полным, поэтому после else печатаем x не равно 1, а значение переменной y может быть любым.
Оператор - переключатель
В отличие от условного оператора переключатель может использоваться для мультиветвления фрагмента исходного кода программы, т.е. фрагмент исходного кода делится на несколько фрагментов и в зависимости от условий выполняется один фрагмент из нескольких (или несколько из нескольких).
Формат переключателя:
switch(<переключающее_выражение>)
{
case <константное_выражение1>: <операторы>
case <константное_выражение2>: <операторы>
……..
case <константное_выражениеN>: <операторы>
[ default: <операторы> ]
}
<переключающее_выражение> - значение целочисленное или приводящееся к целому, например, тип char.
<константное_выражениеX> - целочисленное или приводящееся к целому.
При выполнении переключателя управление передается на подходящую метку вида case <конст_выр>:, значение которой совпадает со значением переключающего выражения или на метку default (она не обязательна), если ни одна из меток не сработала. После передачи управления выполняются все операторы до конца переключателя, вне зависимости от наличия меток. Для выхода из переключателя необходимо использовать оператор break;
Рассмотрим примеры:
int a;
printf("a="); scanf("%d", &a);
switch(a)
{
case 1: printf("\n1");
case 2: printf("\n2");
case 3: printf("\n3");
default:
printf("\n>3");
}
В данном фрагменте при вводе с клавиатуры значения 2 на печать будет выводиться:
>3
Т.е. управление передается на метку case 2: и далее выполняются все последующие операторы, не смотря на наличие меток, т.е. работает принцип выбора несколько из нескольких. Чаще всего требуется реализовать принцип выбора – один из нескольких. Для этого в переключателе необходимо использовать оператор break; - оператор выхода из цикла или переключателя. Если приведенный выше пример переписать:
int a;
printf("a="); scanf_s("%d", &a);
switch(a)
{
case 1: printf("\n1"); break;
case 2: printf("\n2"); break;
case 3: printf("\n3"); break;
default:
printf("\n>3");
}
то при вводе с клавиатуры значения 2 будет выводиться значение 2, затем осуществляется выход из переключателя оператором break;