Давыдов В.Г. - Программирование и основы алгоритмизации - 2003
.pdf5. Пусть определен массив
±nt |
а[ 25 |
]; |
Напишите фрагмент Си-программы, который напечатает с новой строки значения элементов массива "а" по пять элементов в строке и по десять позиций на элемент. Решить задачу с помощью цикла while.
6. При каких исходных значениях "А:" приведенный ниже цикл бу дет выполняться бесконечно?
±zit |
к; |
wh±lG( |
к < 5 ) к+ + ; |
5.ВЫРАЖЕНИЯ И ОПЕРАЦИИ
Вязыках Си/С++ предусмотрен богатый набор операций. В дополнение к традиционным арифметическим, логическим операци ям, операциям отношения и присваивания предусмотрены сокра щенные версии этих операций, побитовые операции и операции над адресами (указателями). Рассмотрим операции и выражения, ис пользующие их, кроме операций над адресами и побитовых опера ций, которые будут рассмотрены ниже.
Можно выделить шесть категорий операций:
•ссылки;
•унарные операции;
•бинарные операции;
•тернарные операции;
•присваивания;
•операция "запятая".
Операции ссылки используются, в основном, для доступа к элементам массивов и структур.
Унарные операции воздействуют на одно значение или выра жение.
В бинарных операциях участвуют два выражения, а в тернар ных - три.
Приоритеты операций в порядке их убывания и порядок вы полнения операций с одинаковым приоритетом указаны в табл. 16. Из приведенной таблицы следует, что в особый класс бинарных операций выделены операции присваивания.
Табл. 16. Приоритеты и порядок выполнения операций
Наименование |
Знаки операций |
Порядок |
операций |
|
выпол |
|
|
нения |
Разрешение области |
|
Нет |
видимости |
|
|
Ссылочные |
[ ] - доступ по индексу: адрес_начала[ выражение ] |
Слева - |
|
или выражение[ адрес_начала ]) |
направо |
|
( ) — управление порядком выполнения операций в |
|
|
выражении; вызов функции: |
|
|
имя_функции( списокпараметров ); |
|
|
конструирование значения: |
|
|
тип(списокпараметров) |
|
|
. — выбор члена класса посредством объекта: |
|
|
объект, членкласса |
|
|
-> - выбор члена класса посредством указателя: |
|
|
указатель->член класса |
|
111
Наименование
операций
Унарные
Мультипликативные
бинарные
Аддитивные бинарные
Сдвига бинарные
продолжение табл. 16
Знаки операций |
Порядок |
|
выпол |
|
нения |
++ - постфиксный инкремент: lvalue++ |
Нет |
— постфиксный декремент: lvalue- |
|
new - динамически создать объект (выделить |
|
динамическую память): new type или |
|
new type( списоквыражений) |
|
delete — уничтожить объект (освободить |
|
динамическую память): delete указа- |
|
гел ьнаобъект |
|
delete[ ] - уничтожить массив объектов: |
|
delete указательнамассивобъектов |
|
++- префиксный инкремент: -ь+lvalue
—префиксный декремент: —lvalue
*- разадресация (разименование): * выражение
&- получение адреса объекта: & lvalue
+- унарный плюс: + выражение
—- унарный минус: - выражение
!- логическое отрицание (not): !выражение '- - поразрядное дополнение: -выражение sizeof - размер в байтах: 812еоГ(объект) или sizeof(THn)
typeidO - идентификация типа времени выполнения: typeid(type)
(type) - приведение типа: (type)выpaжeниe - приведение типа выражения к типу в скобках (старый стиль), выполняется справа-налево
COnst_cast — константное преобразование типа: const_cast<type>(выpaжeниe)
dynamic_cast - преобразование типа с проверкой во время выполнения:
dynamiccast <1уре>(выражение) reinterpret__cast - преобразование типа без проверки: reinteфret_cast<type>(выpaжeниe)
static__cast — преобразование типа с проверкой во время компиляции: static cast<type>(выpaжeниe)
.* - выбор члена класса посредством объекта: объект. *указатель_на_член_класса, выполняется слева-направо
->* - выбор члена класса посредством указателя на объект:
указатель на объект ->* указатель на член класса,
выполняется слева-направо
* - умножение: выражение * выражение |
Слева - |
||
/ - деление: выражение / выражение |
| направо |
||
% - остаток от деления (деление по модулю): |
|
||
выражение % выражение |
|
|
|
+ - сложение: выражение -^ выражение |
Слева - |
||
- - вычитание: выражение - выражение |
направо |
||
« |
- сдвиг влево: выражение « |
выражение |
Слева - |
» |
- сдвиг вправо: выражение » |
выражение |
направо |
112
Продолжение табл. 16
1 |
Наименование |
|
Знаки операций |
Порядок |
|
|
|
операций |
|
|
|
выпол |
|
|
Отношения бинарные |
|
|
|
нения |
|
|
< - меньше: выражение < выражение |
Слева — |
|
|||
|
|
> - больше: выражение > выражение |
направо |
|
||
|
|
<= - меньше или равно: выражение <= выражение |
|
|
||
|
Отношения бинарные |
>= - больше или равно: выражение >= выражение |
Слева — |
|
||
|
== - равно: выражение == выражение |
|
||||
|
|
!= - не равно: выражение != выражение |
направо |
| |
||
|
Поразрядная "И" |
& - поразрядное умножение: |
Слева - |
|
||
|
бинарная |
выражение & выражение |
направо |
|
||
|
Поразрядная |
^ - выражение '^ выражение |
Слева - |
|
||
|
"ИСКЛЮЧАЮЩЕЕ |
|
|
|
направо |
|
|
ИЛИ" бинарная |
|
|
|
|
|
|
Поразрядная |
1 - выражение | выражение |
Слева - |
|
||
|
"ВКЛЮЧАЮЩЕЕ |
|
|
|
направо |
|
|
ИЛИ" бинарная |
|
|
|
Слева - |
|
|
Логическая "И" |
&& - логическое умножение: |
|
|||
|
бинарная(and) |
выражение && выражение |
направо |
|
||
1 |
Логическая "ИЛИ" |
ii - логическое умножение: выражение || выражение |
Слева - |
|
||
|
бинарная (or) |
|
|
|
направо |
|
|
Условная тернарР1ая |
?: - выражение ? выражение : выражение |
Справа- |
|
||
|
|
|
|
|
налево |
|
|
Простое присваивание |
= - lvalue = выражение |
Справа — |
|
||
|
|
|
|
|
налево |
|
|
Совмещенное |
*= - выражение *= выражение |
Справа — |
|
||
|
присваивание |
/= - выражение /= выражение |
налево |
|
||
|
|
%= - выражение %= выражение |
|
|
||
|
|
+= - выражение += выражение |
|
|
||
|
|
-= - выражение -= выражение |
|
|
||
|
|
« = |
- выражение « = |
выражение |
|
|
|
|
» = |
- выражение » = |
выражение |
|
|
|
|
&= - выражение &= выражение |
|
|
||
|
|
1= - выражение |= выражение |
|
|
||
Генерация исключения |
^= - выражение ^= выражение |
Нет |
|
|||
throw - throw выражение |
|
|||||
|
Запятая |
, - выражение , выражение |
Слева - |
|
||
|
(последовательность) |
|
|
|
направо |
| |
5.1. Операции ссылки
Порядок выполнения операций (раньше - позже) определяется их приоритетом. Операции с одинаковым приоритетом могут вы полняться в порядке их появления в выражении слева - направо или справа - налево. В этом плане операции ссылки после операции раз решения области видимости имеют наивысший приоритет и выпол няются слева - направо (табл. 16).
Имеются следующие разновидности операций ссылки:
• ссылка на элемент массива [ ];
113
•ссылка на элемент структуры .;
•ссылка на элемент структуры с помощью указателя ->.
Особое место в этой группе занимает операция "()", служащая для управления порядком выполнения операций в выражении. Объ ясняется это тем, что данная операция, как и другие операции ссыл ки, имеет наивысший приоритет.
Ссылка на элемент массива. Операция предназначена для выделения конкретного элемента массива. Чтобы использовать эту операцию, выражение, называемое индексным и имеющее целое значение, заключают в квадратные скобки и записывают после име ни массива:
^define |
N100 |
|
// |
Размер |
массива |
|
|
||
±nt |
|
|
|
arr[ N ]; // |
Определение |
массива |
целого |
типа |
|
. . . |
агг[ |
i+2 |
J |
// |
из N |
элементов |
массива |
|
|
// |
Ссылка |
на |
элемент |
|
|||||
. . . |
arrf |
12 |
] |
// |
Ссылка |
на |
элемент |
массива |
|
Повторно напомним, что у массива, содержащего N элементов, значения индекса изменяются в диапазоне от О до Л^-1. Имя массива, записанное без операции ссылки, означает адрес первого элемента массива:
агг эквивалентно <&агг/" О ]
Алгоритм выполнения ссылки на элемент массива следующий (например, для агг[ i+2 ]):
1. Вычислить значение выражения, которое служит индексом. Пусть, например, результат s = i+2.
2. Преобразовать s в сдвиг в массиве элемента с индексом /+2 относительно начала массива. Для этого следует умножить s на раз мер отдельного элемента массива:
shift = S '* s±zeof( ±nt )
|
3. |
Вычислить адрес элемента массива по формуле: |
|
|
||||
adr |
= |
arr+shift |
= arr+s*slzeof |
(int) |
= &arr |
[ 0] -hs'^slzeof |
(int) |
|
|
Эта функция |
получила название |
функции |
индексации. |
В |
ней |
||
агг |
= 8сагг[ О ] означает адрес первого элемента массива (адрес |
на |
||||||
чала массива). |
|
|
|
|
|
|
||
|
4. |
Извлечь значение, находящееся по этому адресу. |
|
|
||||
|
Нетрудно заметить, что для доступа к элементам одномерного |
массива нужно выполнить довольно много работы - одно умножение
114
и одно сложение. Поэтому работа с массивом по сравнению со ci^- лярными объектами происходит существенно медленнее и это сле дует иметь в виду.
Получим функцию индексации для двумерного массива. Дву мерный массив (в математике его называют матрицей) располагает ся в оперативной памяти по строкам - сначала располагаются эле менты первой строки матрицы в порядке возрастания индексов и адресов оперативной памяти, затем - второй строки и т.д.
#define |
N10 |
|
|
|
// |
Строчный |
размер |
матрицы |
(массива) |
||||||
#define |
М 9 |
|
|
|
// |
Столбцовый |
размер |
массива |
|
||||||
// |
Определение |
двумерного |
массива |
|
|
|
|
|
|
||||||
short |
±nt |
|
two_arr[ |
N ][ |
М |
]/ |
|
|
|
|
|
|
|||
// |
Функция |
индексации |
для |
элемента |
массива |
two__arr[i] |
[j] : |
||||||||
// |
1. |
Вычисляем |
значения |
индексных |
выражений, |
указанных |
в |
||||||||
// |
квадратных |
скобках |
(в |
данном |
случае |
этого |
не требуется) . |
||||||||
// |
2. |
Вычисляем |
сдвиг |
указанного |
|
элемента |
относительно |
начала |
|||||||
// |
массива |
shift |
= (i*M+j) |
*s±zeo£(shoirb |
int) . |
|
|||||||||
// |
3, |
Тогда |
функция |
индексации, |
|
дающая |
адрес |
оперативной |
|||||||
// |
памяти, |
по |
которому |
находится |
|
элемент |
|
массива |
|
||||||
// |
two__aгг |
[i ] [j ] , |
имеет следующий |
вид: |
|
(i*M+j) |
|
||||||||
// |
adr |
= two_arr-hshift |
= |
8ctwo_arr[0] |
[0] + |
|
|||||||||
// |
*3±zeof( |
short |
i n t |
; . |
|
|
|
|
|
|
|
|
|
Для доступа к элементам двумерного массива вычислительной работы еще больше - на каждый элемент два умножения и два сло жения. Поэтому для повышения быстродействия надо избегать ис пользования массивов вообще и особенно массивов высокой раз мерности.
Ссылка на элемент структуры. Ссылка на элемент структу ры осуществляется с помощью операции, обозначаемой точкой. Вы ражение
current .stip (см. подразд. 3.9.3 о структурах)
представляет значение элемента stip структуры current. Так как структуры могут быть вложенными, то вложенность распространя ется и на ссылку на элемент структуры. Например,
bigstr.smallstr.elem
представляет элемент elem структуры smallstr^ которая, в свою оче редь, является элементом структуры bigstr.
Операция ссылки на элемент структуры с помощью указателя выглядит следующим образом:
115
STUDENT__INFO |
s_data, |
// |
|
Структура |
|
|
*ps_data |
= |
&s__data; |
на эту же структуру |
|
|
|
// |
Указатель |
||
. . . s_data.stlp |
/* эквивалентно |
*/ |
ps__data->stlp |
5.2. Унарные операции
Приоритет унарных операций ниже, чем операций ссылок и выполняются они справа налево (см. табл. 16). Унарные операции представлены в табл. 17.
Табл.17. Унарные операции
- |
Инвертирование знака |
f |
Логическое отрицание |
++ — |
Увеличение, уменьшение |
sizeo/{ имя типа ) |
Размер в байтах |
(спецификация типа)выражение |
Преобразование типа |
~ |
Поразрядная инверсия (обсуждается ниже в |
& * |
разделе "Поля битов и побитовые операции") |
Адресация, разадресация (обсуждается ниже |
|
|
в разделе "Указатели") |
Унарный минус. Является обычной арифметической операци ей изменения знака и может использоваться в выражении любого арифметического типа.
При использовании операции "-" тип результирующего значе ния тот же, что и тип операнда, над которым выполняется операция.
Логическое отрицание. Операция отрицания "!" изменяет значение "истина" на значение "ложь", а значение "ложь" - на значе ние "истина". Напомним, что значению "истина" соответствует не нулевое целое значение, а значению "ложь" - нуль.
Увеличение и уменьшение. Операции "++" и "—" могут пред шествовать операнду (префиксные операции) или следовать за ним (постфиксные операции). Эти операции соответственно добавляют или вычитают единицу из значения операнда и присваивают ему по лученный результат:
|
|
X |
= |
X |
-h |
1; |
/'^ |
эквивалентно |
*•/ |
++х; |
|
|
|
у |
= |
у |
- |
1; |
/* |
эквивалентно |
V |
--у; |
|
X |
= |
X |
+ |
1; |
|
|
/* |
эквивалентны |
|
= arrf |
-h-i-x 7/ |
а |
= |
а г г |
[ |
X |
]; |
|
|
|
|
|
116
а |
= |
arr |
[ X ]; |
/* эквивалентны |
*/ |
а = arr [ к + + ]; |
к |
= |
к |
+ 1; |
|
|
|
При использовании "++" и "—" тип результирующего значения тот же, что и тип операнда, над которым выполняется операция.
Преобразование |
типа. В языке |
предусмотрено средство для |
явного изменения типа выражения: |
|
|
( |
спецификация_типа |
)выражение |
"Спецификациятипа" может быть любым служебным словом, задающим спецификацию типа, например, int, short, long, float и т.д.
int |
|
|
|
|
1 |
= |
11000, |
|
w = |
4; |
|
|
|
|
long |
|
|
|
a; |
|
|
|
|
|
|
|
|
||
// |
Если целое |
занимает 2 |
байта |
(разрядность |
процессора |
16 |
||||||||
// |
|
|
бит), |
то |
здесь |
возникает |
ошибка при |
умножении: |
|
|||||
// |
= |
1 |
44000 |
> |
32767. |
Как |
быть? |
|
|
|
|
|
||
а |
"^ w; |
) ( |
1 |
* W ) ; |
// |
И |
здесь |
возникает |
ошибка |
при |
||||
а |
= |
( |
long |
|||||||||||
а |
= |
( |
long |
)1 |
* |
w; |
|
// |
|
умножении: |
44000 |
> 32 767 |
||
|
// |
Так |
правильно! |
|
|
Другим распространенным примером использования автома тического преобразования типа является преобразование типов ар гументов при вызове библиотечных функций языка Си. И еще одно важное замечание. В вызовах таких функций аргументы с типом float автоматически преобразуются к типу double, а аргументы с ти пом char преобразуются к типу int.
Операция sizeof Операция выполняется на этапе компиляции программы и дает константу, которая равна числу байтов, требуе мых для хранения в памяти данного объекта. Объектом может быть имя переменной, массива, структуры или просто спецификация ти па. Применение этой операции демонстрировалось выше.
Пример.
±пЬ |
count, |
iarrayl |
10 |
]; |
) / sizeof |
( ±nt |
|
|
for( count |
= 0; count |
< slzeof( |
|
larray |
) |
; |
||
count++ |
) |
= %d\n", |
count, |
iarrayl |
count |
] |
) ; |
|
printf( |
"iarray[%d] |
Применение операции sizeof всюду, rjxQ это возможно, счита ется хорошим стилем программирования.
117
5.3. Бинарные операции
п р и о р и т е т и порядок выполнения бинарных операций пред ставлены в табл. 16. Бинарные операции воздействуют на два выра жения:
выражение Ыпор выражение
Здесь Ыпор - одна из бинарных операций, приведенных в табл.
18. |
Табл. 18. Бинарные операции |
|
|
, /, % |
Умножение, деление, взятие остатка от деления целого на целое |
|
Сложение, вычитание |
» |
Операции сдвига (обсуждаются в разделе "Поля битов и побитовые |
операции") |
|
|
Больше, меньше |
|
Больше или равно |
|
Меньше или равно |
|
Равно |
&, м |
Не равно |
Поразрядные логические операции (обсуждаются в разделе "Поля битов |
|
и побитовые операции") |
|
&&, |
Логическое И, логическое ИЛИ |
Арифметические |
операции: |
"*", "/", "%", "+" и "-". Эти опе |
|||
рации задают обычные действия над операндами |
арифметического |
||||
типа. Операция "%" означает получение |
остатка |
от деления одного |
|||
г^елого числа на другое: |
|
|
|
||
1 |
% j |
дает значение |
i - |
( i/j ) |
* j |
Примеры. |
12 % 6 дает О, 13 % 6 дает |
1, 3 % 6 дает 3 и т.д. |
Если арифметическая операция или операция отношения со держит операнды различных типов, то компилятор выполняет авто матическое преобразование их типов, если замена типов явно не указана. Такое преобразование производится путем "продвижения"
значений операндов к "наибольшему" типу: |
|
|
||||
long |
double |
(наибольший |
тип) |
|||
|
|
double |
|
|
|
|
|
|
float |
|
|
|
|
|
unsigned |
long |
int |
|
|
|
|
|
long |
int |
|
|
|
|
unsigned |
int |
|
|
|
|
|
|
int |
|
|
|
|
unsigned. |
char |
или unsigned |
|
short |
int |
|
ciiax- или |
short |
int |
(наименьший |
тип) |
118
Алгоритм выполнения очередной бинарной арифметической операции ("*", "/", "+", "-") или операции отношения ("<", ">", ">=", "<=", "==", "!=") состоит в следующем.
1. Если один операнд имеет тип long double, то и второй опе ранд преобразуется к типу long double и выполняется операция. Иначе производится переход к п. 2.
2.Все операнды с типом float преобразуются к типу double и производится переход к п. 3.
3.Если один операнд имеет тип double, то и второй операнд преобразуется к этому же типу и выполняется операция. Иначе вы полняется п. 4.
4.Если один операнд имеет тип unsigned long int, то и второй операнд преобразуется к этому же типу и выполняется операция. Иначе выполняется п. 5.
5.Если один операнд имеет тип long int, то и второй операнд преобразуется к этому же типу и выполняется операция. Иначе вы полняется п. 6.
6.Если один операнд имеет тип unsigned int, то и второй опе ранд преобразуется к этому же типу и выполняется операция. Иначе выполняется п. 7.
7.Если операнды имеют тип unsigned char и/или unsigned short int, то они преобразуются к типу unsigned int и выполняется опера ция. Иначе выполняется п. 8.
8.Если операнды имеют тип char и/или short int, то они пре образуются к типу int и выполняется операция.
Далее по такому же алгоритму выполняется следующая из пе речисленных выше арифметических операций и операций отноше ний.
Результат арифметической операции имеет такой же тип, как и тип операндов после преобразования. Результат операции отноше ния всегда имеет тип int (О - "ложь", "нет" и 1 - "истина", "да").
И еще раз повторим важное замечание. В вызовах функций ар гументы с типом float автоматически преобразуются к типу double, а аргументы с типом char преобразуются к типу int.
Операции отношения. Приоритеты и порядок выполнения операций отношения "<", ">", "<=", ">=", == и "!=** указаны в табл. 16. Операция "==" выполняет проверку на равенство, а операция "!=" - проверку на неравенство. Смысл остальных операций отноше ния очевиден.
Запись операции отношения имеет вид:
операнд! |
операция__отношения |
опера нд2 |
119