Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
295
Добавлен:
23.03.2016
Размер:
306.22 Кб
Скачать

СПРАВОЧНЫЙ МАТЕРИАЛ

Книга по работе

 

Роман Абраш

 

 

 

 

 

 

 

 

с WinAVR и AVR Studio

 

г. Новочеркасск

 

E-mail: arv@radioliga.com

 

 

 

 

 

 

 

%

Остаток от целочисленного деления операндов

 

 

Продолжение. Начало в №1/2010

 

 

 

 

 

 

|

Побитовая операция ИЛИ над операндами

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Указатели

 

&

Побитовая операция И над операндами

Указатель – это переменная особого рода: она содержит не

 

^

Побитовая операция ИСКЛЮЧАЮЩЕЕ ИЛИ над

само значение переменной, а адрес той области памяти, где иско-

 

 

операндами

мое значение хранится.

 

>>

Побитовый сдвиг влево

Указатель – это одна из наиболее мощных возможностей язы-

 

<<

Побитовый сдвиг вправо

ка Си и, как любое мощное средство, указатель может быть как

 

Об операциях побитового сдвига следует упомянуть, что эти опе-

чрезвычайно удобным инструментом, так и источником больших

 

рации сохраняют знак операнда, если операнд имеет знаковый тип.

проблем, если используется неумело.

 

Унарная логическая операция всего одна:

Для объявления указателя достаточно добавить к типу пере-

 

Знак

Операция

менной символ «*»:

 

!

Логическое отрицание (НЕ)

 

 

 

 

 

 

 

Логических операций над парой операндов больше:

unsigned int * iptr;

 

 

 

 

Знак

Операция

char * arr[];

 

 

||

Логическое ИЛИ

long * lptr;

 

 

&&

Логическое И

Объявленные таким образом переменные iptr, arr[] и lptr будут

 

>

Больше

указателями: iptr – указатель на число типа int, arr массив указа-

 

<

Меньше

телей на символ, lptr – указатель на число типа long.

 

==

Эквивалентно

Чтобы обратиться к тем данным, на которые указывает пере-

 

!=

Не эквивалентно

менная-указатель, следует предварить ее имя символом «*» (такая

 

<=

Меньше или равно

операция получила название «разыменование указателя»):

 

>=

Больше или равно

iptr – адрес области памяти, хранящей число типа int

 

Важно понимать, что для логических выражений определено

*iptr – значение этого числа

 

лишь два значения результата: ИСТИНА и ЛОЖЬ, причем истин-

Очевидно, что использование указателей позволит получить

 

ным в Си считается любое не равное нулю значение, а ложным –

доступ к любой произвольной области памяти. Именно в этом и

 

значение ноль.

кроется источник возможных проблем: стоит ошибиться в значе-

 

Вот несколько примеров логических и арифметических выра-

нии переменной-указателя, как последствия обращения к данным,

 

жений:

 

 

 

на которые она указывает, становятся непредсказуемыми.

 

 

 

 

 

5 + 2 * 3 – арифметическое выражение, эквивалентное 11

 

 

 

 

 

 

 

 

 

Выражения

 

(5 + 2) * 3 – арифметическое выражение, эквивалентное 21

 

 

5 + var * (3 + b) – арифметическое выражение с использованием переменных

 

Выражение – это запись алгоритма вычислений над операн-

 

 

дами при помощи знаков математических, логических и прочих дей-

 

(5 > 2) – логическое выражение со значением ЛОЖЬ

 

ствий. Другими словами: выражение – есть формула вычислений,

 

(2-7) >= -11 – логическое выражение со значением ИСТИНА

 

записанная средствами языка программирования.

 

(2-7) >= 0 – логическое выражение со значением ЛОЖЬ

 

 

 

 

 

В определении выражения использован новый термин: операнд.

 

Знаки арифметических и логических операций называются

Операнд – это выражение, над которым выполняется какое-либо

 

по-другому операторами, однако понятие оператор более ши-

действие. Как видите, операнд и выражение оказываются опреде-

 

рокое, и включает в себя не только какое либо одно действие,

ленными друг через друга. Надеюсь, для образованного человека

 

но и целую группу действий, выполняемых по определенным

не составит труда представить себе, что же такое выражение на

 

правилам.

 

 

 

самом деле – для этого достаточно вспомнить, что такое математи-

 

Особняком стоит операция, обозначаемая символом «?» – ус-

ческое выражение.

 

ловная операция. Эта операция имеет достаточно сложную форму

В языке Си определены два типа операндов (выражений): ма-

 

записи:

 

 

 

тематические и логические. Математическое выражение состоит

 

<выражение1> ? <значение1> : <значение2>

из операндов, эквивалентных числам, и знаков математических

 

Операция позволяет выбрать в качестве своего результата одно

операций. Логическое выражение – это выражение отношений, т.е.

 

из значений – значение1 или значение2 в зависимости от того,

ответов на вопросы «больше?», «меньше?», «равно?» и т.д. Ана-

 

истинно или ложно выражение1. В качестве обоих значений могут

логично математическим, существуют логические действия и соот-

 

использоваться так же выражения.

ветствующие им знаки логических операций.

 

Пример:

Действия могут выполняться над двумя операндами или над

 

x > 5 ? 4 : 8 – если x будет меньше или равно 5, то результат

одним операндом – унарные операции.

 

операции будет 8, а если x будет больше 5 – результат будет 4.

В Си определены следующие унарные математические опе-

 

Смысл в выражениях был бы невелик, если бы результат их

рации:

 

 

 

 

 

вычислений было бы невозможно присваивать значениям перемен-

Знак

Операция

 

ных. Для присвоения значения переменной используется оператор

Унарный минус, признак изменения знака числа на

 

присваивания, обозначаемый символом «=»:

 

 

противоположный

 

 

 

 

 

 

 

var = 5 + 2; // присвоить переменной var значение выражения 5 + 2, т.е. 7

 

+

Унарный плюс, означает сохранение знака числа без

 

 

 

 

изменения (принципиального смысла не имеет)

 

Возвращаясь к описанию переменных, следует заметить, что

++

Знак инкремента, увеличение операнда на 1

 

можно присваивать значение переменной сразу в момент ее опи-

--

Знак декремента, уменьшение операнда на 1

 

сания:

 

 

 

~

Побитовая инверсия числа

 

 

 

 

 

int var = 5; // описанная переменная var имеет тип int и значение 5

 

Математических операций над двумя операндами больше:

 

 

 

Знак

Операция

 

В операторе присваивания левее знака «=» должно находиться

+

Сумма операндов

 

так называемое леводопустимое выражение, т.е. либо переменная,

Разность операндов

 

либо такое выражение, результатом которого будет значение ука-

*

Произведение операндов

 

зателя, который покажет место в памяти для хранения результата.

/

Частное операндов

 

Правее знака равенства может находиться любое праводопустимое

 

 

 

 

 

 

 

 

 

5 7

Радиолюбитель – 02/2010

 

 

 

 

 

 

 

 

 

 

 

 

 

 

СПРАВОЧНЫЙ МАТЕРИАЛ

выражение, т.е. в сущности, любое выражение. Интересный момент

* / %

 

12

возникает, если и слева и справа от знака равенства используется

+ - (

 

11

одна и та же переменная:

 

 

>> и <<

 

10

 

 

 

 

<

<= > >=

 

9

S = S + 5;

 

 

 

 

 

 

 

==

!=

 

8

Такое выражение следует понимать так: «присвоить перемен-

 

&

 

 

7

ной S значение, равное сумме ее текущего значения и числа 5».

^

 

 

6

Можно записать то же самое более коротким способом:

|

 

 

5

 

 

 

 

&&

 

 

4

S += 5;

 

 

 

 

 

 

 

 

||

 

 

3

 

 

 

 

 

 

В данном примере использован двойной оператор «+=», назы-

?

 

 

2

ваемый присваивание с суммированием. Значение этого операто-

= *= /= += -=

&= ^= |= <<= >>=

1

ра то же самое: увеличение значения переменной на число правее

Так как запомнить все приоритеты довольно непросто, жела-

оператора. Кроме присваивания с суммированием допустимы ана-

тельно на первых порах (и не возбраняется вообще всегда) указы-

логичные комбинации для присваивания с разностью, с умножени-

вать порядок вычислений при помощи скобок.

 

ем, делением или остатком от деления, записываемым соответ-

Использование операторов ++ и -- совместно с указателями

ственно так: «–=», «*=», «/=» или «%=». Любой такой оператор «раз-

приводит к необходимости помнить о порядке выполнения опе-

ворачивается» аналогично рассмотренному:

раций:

 

 

 

 

 

 

 

 

 

a *= 2

равносильно

a = a * 2

 

// вариант 1

 

 

a /= 2

равносильно

a = a / 2

 

ptr = &temp;

 

 

a -= 2

равносильно

a = a – 2

 

var = *ptr++;

 

 

a %= 2

равносильно

a = a % 2

 

// вариант 2

 

 

Несколько слов о присваивании значений переменным типа

ptr = &temp;

 

 

указатель. Как было сказано, указатель есть ни что иное, как адрес

var = (*ptr)++;

 

 

 

 

 

объекта в памяти, соответственно в качестве значения можно ему

Оба варианта дадут одно и то же значение var, но совсем раз-

присваивать только адрес. Делается это при помощи унарного опе-

ные значений для temp и ptr: в первом случае переменная temp не

ратора взятия адреса & (не путать с побитовой операцией И !!!):

изменится, но увеличится на 1 значение указателя ptr; во втором

 

 

 

 

случае неизменным останется значение указателя, но изменится

int *ptr;

 

 

 

 

 

 

содержимое переменной, на которую он указывает, т.е. temp уве-

int var;

 

 

 

 

 

 

личится на 1.

 

 

ptr = &var;

 

 

 

Операторы ++ и -- с указателями работают особым образом:

 

 

 

они изменяют значение указателя на величину, равную размеру

 

 

 

 

В этом примере объявлен указатель ptr на переменную типа int

типа элемента, на который указатель указывает. То есть если ука-

и переменная var этого типа; далее происходит присваивание ука-

затель объявлен как int *ptr, то ptr++ изменит значение указателя

зателю значения адреса переменной var.

на 2, если long *ptr, то ptr++ увеличит указатель на 4 и т.д. Увеличе-

Теперь операторы

 

 

ние указателя на 1 происходит только для типа указателя (void *)

 

 

 

 

(«абстрактный указатель», просто адрес байта) или (char *).

*ptr = 12;

 

 

 

 

 

 

В любом выражении допустимо комбинировать логические и

 

 

 

 

и

 

 

 

арифметические выражения, в этом случае следует считать, что

 

 

 

 

логическому значению ЛОЖЬ соответствует арифметическое зна-

var = 12;

 

 

 

 

 

 

чение ноль, а значению ИСТИНА – арифметическое значение 1:

приведут к одинаковому результату.

 

 

 

 

var = (5 < 2) + 2;

// переменной var будет присвоено значение 2

Иногда требуется обратиться к какому-либо участку памяти,

который явно не является конкретной переменной, т.е. адрес учас-

var = (5 >= 2) + 2;

// переменной var будет присвоено значение 3

тка известен, но соответствующего ему описанного в программе

Особое внимание следует обращать на приоритет операций:

объекта нет. В этом случае следует использовать прием, назван-

 

 

 

 

var = (5 > 2) + 2;

// var = 3

 

ный явным приведением типа:

 

 

 

 

 

 

 

var = 5 > 2 + 2;

// var = 0

 

ptr = (int *)0x1000;

 

 

 

 

 

Во втором выражении более высокий приоритет имеет ариф-

 

 

 

 

Пример показывает, как указателю ptr присваивается значе-

метическая операция сложения, поэтому переменной var будет

ние адреса 0x1000, т.е. происходит «обман» бдительного компиля-

присвоено значение выражения 5 > 4, что ЛОЖНО, т.е. var будет

тора: ему подсовывают указание воспринимать число, как адрес

равна 0.

 

 

переменной типа int, хотя не известно, что именно находится по

Наиболее простым и универсальным советом будет использо-

этому адресу фактически. Приведение типов приходится делать

вание скобок везде, где необходимо точно гарантировать поря-

всегда, когда типы операндов не соответствуют желаемым.

док вычислений – в этом случае можно считать (конечно, лишь

 

 

 

 

условно!) все операторы равноприоритетными, а порядок вычис-

Приоритеты операций

 

 

лений определять лишь с помощью скобок. Возможно, это приве-

Каждая операция имеет свой приоритет, который определяет

дет к некоторой излишней «скобочности» программы, зато гаран-

порядок вычисления операции в выражении: если слева направо

тирует, что планы программиста не разойдутся с мнением компи-

следуют подряд несколько операций одинакового приоритета,

лятора.

 

 

порядок их вычисления не определен, но результат вычисления

Упомянутые ранее унарные операторы инкремента и декремен-

гарантированно будет верным, в противном случае вычисляются

та – одна из интересных особенностей языка Си. Эти операторы

прежде операции с наибольшим приоритетом, а затем – с мень-

могут применяться только к операнду-переменной, и ни в коем слу-

шим. Порядок вычислений может быть изменен при помощи круг-

чае не к операнду-константе. Но главное, они могут быть префик-

лых скобок: выражение в скобках вычисляется всегда прежде ос-

сными и постфиксными, т.е. записываться либо слева от пере-

тальных, т.е. имеет наивысший приоритет.

менной, либо справа:

 

 

В таблице перечислены все операции с указанием их приори-

 

 

 

 

int var = 1;

 

 

тетов:

 

 

 

 

 

Операция

 

Приоритет

var++;

// теперь var = 2

 

! ~ «унарные плюс и минус» ++ -- ? операция

--var;

// теперь var = 1

 

разименования указателя *

13

 

var--;

// var = 0

 

5 8

Радиолюбитель – 02/2010

 

СПРАВОЧНЫЙ МАТЕРИАЛ

Казалось бы, к чему два способа выполнить одно и то же? Все

Операторы

дело в том, как будет использован результат оператора при вычис-

Оператор if

лении выражений. Вот пример:

Условный оператор if позволяет выполнить группу операторов

 

 

 

лишь в случае, если какое-то логическое выражение имеет значе-

int var = 5;

 

 

 

 

ние ИСТИНА. Дополнительно может быть определена группа опе-

int b = var++ - 5;

// var становится равным 6

 

раторов, выполняемых в случае, если заданное выражение имеет

int ñ = ++var – 5;

// var становится равным 7

 

значение ЛОЖЬ.

 

 

Переменная b окажется равной 0, а переменная с будет рав-

Оператор if может быть описан по одному из следующих шаб-

на 2. Все дело в том, что при постфиксном операторе ++ снача-

лонов:

ла используется значение переменной в выражении, а потом вы-

1. if (<проверка>) <оператор1>;

полняется оператор инкремента, а при префиксной записи – сна-

2. if (<проверка>) <оператор1>; else <оператор2>;

чала выполняется оператор инкремента, а потом уже обновлен-

3. if (<проверка>) { <список операторов1 ;>}

ное значение переменной используется при вычислении выра-

4. if (<проверка>) { <список операторов1 ;>} else {список

жения.

 

 

операторов2 ;}

Операторы побитового сдвига << и >> выполняют перемеще-

Здесь проверка – это логическое выражение, проверяемое на

ние битов операнда слева на количество разрядов, заданное выра-

истинность, оператор1 – оператор, выполняемый только в случае

жением справа. Как уже было сказано, эти операторы сохраняют

истинности проверки, оператор2 выполняется только в случае лож-

знак левого операнда, если операнд – число со знаком. Заполне-

ности проверки. Под списком операторов подразумевается любая

ние «освобождаемых» разрядов для беззнаковых чисел происхо-

последовательность любых операторов, причем, как и одиночные,

дит нулями.

 

 

эти списки выполняются лишь в соответствующем случае в зависи-

Унарные логические операции, и операция побитовой инвер-

мости от результата проверки.

сии имеют лишь один вариант вычисления. Но, к сожалению, на

Обратите внимание, что для 3 и 4 форм оператора if наличие

этом простые операторы заканчиваются, все прочие операторы

завершающей оператор точки с запятой не является необходи-

имеют более сложную форму записи и, соответственно, выпол-

мым. В данном случае закрывающая фигурная скобка однознач-

няют более сложные действия, заслуживая отдельного рассмот-

но свидетельствует о завершении оператора, в то время как в

рения.

 

 

списках каждый из операторов должен завершаться точкой с за-

 

 

 

пятой (если, конечно, оператор в списке не ограничивается фи-

Тип результата выражения

гурными скобками).

Выражения могут состоять из операндов различных типов, на-

Шаблоны оператора if не накладывают никаких ограничений

пример, не возбраняется осуществлять суммирование переменных

на то, какие операторы могут быть на месте оператра1 или опера-

типа char и long, double и unsigned int и т.д. И результат этих вы-

тора2 или же в соответствующих списках. Это означает, что среди

числений так же может быть присвоен переменной любого типа.

них так же может использоваться оператор if. В этом случае может

Как же ведет себя компилятор в этом случае?

возникнуть проблема с определением принадлежности части опе-

Рассмотрим следующий пример:

ратора else:

 

 

 

 

 

 

 

if (a < 2) if (c > 5) result = 5; else result = 0;

int x1 = 20000, x2 = 15;

 

 

long res;

 

 

В этом примере стоит задуматься, к какому из операторов if

res = x1 * x2;

 

 

относится часть else? Язык Си однозначно вводит следующее пра-

 

 

вило: всякий else относится к ближайшему предыдущему if, не име-

Переменной res (длинное целое) присваивается результат про-

ющему своего else. Таким образом, в вышеприведенном примере

изведения двух чисел (int), значения переменных укладываются в

else относится к оператору if(c > 5).

допустимые диапазоны, ожидается, что значение res будет 300000.

Очевидно, что запись оператора в одну строку не способ-

Однако, это ошибочное предположение.

ствует улучшению восприятия программы. Благодаря тому, что

Стандартом Си определено, что тип результата выражения оп-

язык Си допускает использование любых «пробельных» раздели-

ределяется по набольшему типу входящего в него операнда, т.е. в

телей, гораздо более удачной следует считать следующую форму

данном случае результат x1 * x2 будет иметь тип int. Разумеется,

записи:

произведение чисел вызовет переполнение результата, и все, что

 

if (a < 2)

«не влезло» в размер, будет отброшено, т.е. в результате произве-

дение будет равно -27680.

if (c > 5)

result = 5;

Такой странный результат получился из-за несоответствия типа

результата выражения и типа операндов. Чтобы получить верный

else

результат, нужно использовать один из операндов типа long:

result = 0;

 

 

 

Благодаря использованию табуляций вышеприведенный при-

res = (long)x1 * x2;

 

 

 

 

мер даже визуально показывает принадлежность else соответству-

// èëè òàê

 

 

 

 

ющему оператору if.

res = x1 * (long)x2;

 

 

 

 

Следует отметить, что пример показывает одну из проблем, ха-

Такая запись показывает, что компилятор должен воспринимать

рактерную для оператора if – так называемую неполную проверку

операнд x1, как число типа long. Принудительное указание в круг-

вариантов. Она заключается в следующем: если, например, пере-

лых скобках нового типа для выражения называется явным преоб-

менная а равна 5, то переменная result оказывается с неопреде-

разованием или приведением типа. Ошибкой будет попытка за-

ленным значением. Возможно, она получит (или получила) нужное

дать тип для результата всего выражения:

значение в другом операторе, возможно переменная a никогда не

 

 

 

получит значение больше 1 (но тогда, к чему вообще проверка?!),

res = (long) (x1 * x2);

 

 

 

 

однако если это не так – проблем не миновать.

Такая запись ничем не отличается от первоначальной и даст

 

Итак, при использовании оператора if желательно не допускать

неверный результат (т.к. в этой записи явно указано то преобразо-

двусмысленностей его поведения при различных результатах про-

вание, которое компилятор и так делает автоматически).

веряемых выражений. Так же не лишним будет использование фи-

Компилятор всегда приводит автоматически типы всех операн-

гурных скобок для определения границ списка операторов, даже

дов в выражении к наибольшему типу, т.е. от меньшего типа к боль-

если фактически используется единственный оператор.

шему. Важно помнить, что если в выражении все операнды типа

 

char, то они будут приведены к типу int, если нет явного преобразо-

Оператор switch

вания типов. При помощи приведения типа программист может из-

Анализ разных значений переменной при помощи оператора if

менить его поведение, что чревато большими ошибками.

может вылиться в «многоэтажную» конструкцию типа такой:

 

 

 

5 9

Радиолюбитель – 02/2010

 

 

 

 

 

 

 

 

 

СПРАВОЧНЫЙ МАТЕРИАЛ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

for ([список операторов1]; [условие продолжения]; [список

 

 

if (x == 0) x = 5;

 

 

 

 

 

 

 

операторов 2]) [тело цикла];

 

 

else

 

 

 

Здесь список операторов1 – перечень операторов, разделен-

 

 

if (x == 1) x = 12;

 

 

 

 

 

 

 

ных запятыми, которые должны быть выполнены перед началом

 

 

else

 

 

цикла; условие продолжения – логическое выражение, истинное

 

 

if (x == 3) x = 36;

 

 

значение которого есть непременное условие очередной итерации

 

 

// и т.д. до перебора всех вариантов значения х

 

 

цикла; список операторов2 – перечень операторов, разделенных

 

 

 

 

 

Это очень неудобная конструкция. Заменить ее призван опера-

 

запятыми, выполняемых после каждой итерации цикла; тело цик-

 

 

тор switch, который записывается по следующему шаблону:

 

ла – это один оператор или ограниченная фигурными скобками пос-

 

 

switch (<выражение>){

 

ледовательность операторов, выполняемых на каждой итерации.

 

 

case <значение1>: [операторы1];

 

 

Самое важное, что любая из этих частей оператора for может

 

 

case <значение2> : [операторы2];

 

отсутствовать, о чем свидетельствуют квадратные скобки в шаб-

 

 

// и т.д. до конца вариантов значений

 

лоне.

 

 

[default : [операторы по умолчанию]];

 

 

Пример некоторых вариантов оператора цикла for:

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

for (i = 0, s = 0; i < 10; s += i++);

 

 

 

Здесь выражение – это выражение, значения которого анали-

 

 

 

 

зируются, значение1 – это один из вариантов анализируемого ре-

 

for (;;);

 

 

 

зультата выражения, операторы1 - соответствующая этому вари-

 

for (;i < 10;);

 

 

 

 

for (i = 0, j = 9, s = 0; i < 10; i++, j—) s += arr[i][j];

 

 

 

анту обработка, значение2 и операторы2 – это соответственно

 

 

 

 

следующее анализируемое значение и соответствующая ему об-

 

 

Первый цикл примера вычисляет сумму всех чисел от 0 до 9 и

 

 

работка и т.д. Операторы по умолчанию выполняются в том слу-

 

помещает результат в переменной s. Рассмотрим более подробно,

 

 

чае, если выражение имеет значение, не соответствующее ни од-

 

как это происходит.

 

 

ному из определенных в строке с ключевым словом case. Операто-

 

 

Сначала выполняются операторы i=0 и s = 0, которые инициа-

 

 

ры по умолчанию и ключевое слово default могут отсутствовать.

 

лизируют (ранее объявленные) переменные i и s. Если в момент

 

 

Результат анализируемого выражения обязательно должен быть

 

объявления эти переменные уже проинициализированы, эта часть

 

 

одного из целочисленных типов, а в качестве значений case обяза-

 

оператора for может отсутствовать. Затем происходит проверка ус-

 

 

тельно должны использоваться константы или выражения, вычис-

 

ловия выполнения итерации цикла, т.е. проверка i < 10. Разумеет-

 

 

ляемые на основе констант. Допускается использовать символы (в

 

ся, это истинное выражение, и, значит, происходит выполнение тела

 

 

апострофах).

 

цикла – очередная (первая) итерация. В рассматриваемом приме-

 

 

Оператор действует следующим образом: вычисляется значе-

 

ре тело цикла отсутствует, поэтому просто выполняется заверша-

 

 

ние выражения, результат проверяется на совпадение с одним из

 

ющая часть, которая состоит из оператора s += i++, который одно-

 

 

указанных в строчках case. Если найдено совпадение, то, начиная с

 

временно выполняет 2 действия: накапливает в переменной s сум-

 

 

этого места и вплоть до конца оператора (т.е. до закрывающей

 

му значений переменной i, а затем увеличивает значение i на 1.

 

 

фигурной скобки), выполняются все обозначенные операторы, в том

 

После этих действий снова осуществляется проверка условия, за-

 

 

числе и те, которые относятся к другим, «нижеследующим» за най-

 

тем очередная итерация и т.д. до тех пор, пока значение i не станет

 

 

денным значениям. Если совпадений не найдено, то выполнятся

 

равным 10. В этот момент условие i < 10 станет ложным, и выполне-

 

 

операторы по умолчанию, если они определены, в противном слу-

 

ние оператора for закончится.

 

 

чае не будет выполнено никаких действий. Пример:

 

 

Продолжим рассмотрение примеров операторов for. Второй

 

 

 

 

 

 

 

цикл, единожды начавшись, никогда не закончится, т.е. это беско-

 

 

int res;

 

 

 

 

 

 

нечный оператор, или бесконечный цикл. В этом случае отсутствие

 

 

switch (x){

 

 

выражения проверки условия трактуется как истинное выраже-

 

 

case 0 : res = 1;

 

 

ние, т.е. «нет требований к условию – значит, любые варианты

 

 

case 1 : res = 2;

 

 

подходят».

 

 

default : res = 3;

 

 

 

Третий пример показывает оператор цикла, который может быть

 

 

}

 

 

 

 

бесконечным, если переменная i < 10. Т.е. для того, чтобы этот цикл

 

 

 

 

 

 

 

 

 

Если x в этом примере равен 5, то значение переменной res

 

не смог «завесить» программу, необходимо, чтобы либо к началу

 

 

станет равно 3. Однако, если x будет равен 0, то res все равно ока-

 

цикла переменная i уже содержала значение 10 или более, либо

 

 

жется равным 3, т.к. поочередно выполнятся все операторы, начи-

 

каким-то образом во время выполнения цикла значение i должно

 

 

ная с того, что указан в строке case 0. Чтобы этого бессмысленного

 

быть изменено.

 

 

поведения не происходило, введен особый оператор break, кото-

 

 

Наконец, четвертый пример показывает, как вычислить сумму

 

 

рый вызывает прекращение работы оператора switch досрочно, т.е.

 

«правой» диагонали матрицы arr размером 10x10.

 

 

этот оператор является последним исполняемым оператором внут-

 

 

Следует отметить, что допускается11 внутри списка операто-

 

 

ри switch. Таким образом, правильная запись желаемого алгорит-

 

ров1 использовать определение переменных, однако, определен-

 

 

ма должна быть такой:

 

ные таким образом переменные, считаются существующими толь-

 

 

 

 

 

 

 

ко пока цикл выполняется, и «исчезают» после его завершения:

 

 

int res;

 

 

 

 

 

 

 

for (int i=0; i < 10; i++) a += i;

 

 

switch (x){

 

 

 

Оператор for имеет довольно много вариантов и возможнос-

 

 

case 0 : res = 1; break;

 

 

тей, однако не стоит увлекаться предоставляемой гибкостью. Чем

 

 

case 1 : res = 2; break;

 

 

более простая форма оператора будет избрана, тем меньше веро-

 

 

default : res = 3;

 

 

ятность ошибки программирования:

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

// первый способ

 

 

 

 

 

 

 

В этом случае для x равного 0, 1 и 5 будут получены соответ-

 

 

 

 

 

 

 

 

ственно значения res 1, 2 и 3. Для последнего варианта наличие

 

for (int i, j=9, s; i < 10; s += arr[i++][j—]);

 

 

 

«прекращающего» оператора не требуется, это очевидно.

 

// второй способ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Оператор for

 

int i=0, s=0, î = 9;

 

 

 

 

for (; i<10; i++) {

 

 

 

При реализации многих алгоритмов бывает необходимо повто-

 

 

 

 

рить некую последовательность действий несколько раз. Такое по-

 

 

s += arr[i][j];

 

 

 

вторение получило наименование цикла, а оператор, для соответ-

 

}

j—;

 

 

 

ствующего действия – оператором цикла.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

В Си имеется несколько операторов цикла, один из которых –

 

 

 

 

 

 

 

11 Такое допущение для компилятора WinAVR возможно лишь в том случае, если

 

 

оператор for. Шаблон описания оператора следующий:

 

включен режим соответствия стандарту С99 с расширениями GNU.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

6 0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Радиолюбитель – 02/2010

 

 

 

 

 

 

 

 

 

 

 

 

СПРАВОЧНЫЙ МАТЕРИАЛ

 

В этом примере приведены два варианта ранее рассмотренно-

Оператор continue

го подсчета суммы «правой» диагонали матрицы, однако первый

Оператор continue служит для досрочного начала очередной

способ требует гораздо больше умственных усилий для понимания

итерации цикла, т.е. он обеспечивает немедленный переход к про-

написанного, чем второй.

верке условия для циклов do или while, или к переходу к выполне-

 

 

 

нию последней группы операторов, указанных в круглых скобках,

 

Оператор while

для цикла for.

 

Оператор for удобен, если требуется повторить какие-либо

Этот оператор служит для пропуска части операторов цик-

действия определенное число раз. Но иногда число повторений

ла, если на определенной итерации их выполнять не следует.

неизвестно, хотя известно условие завершения цикла (например,

Пример:

при решении уравнения методом последовательных приближений).

 

 

 

 

int arr[10];

 

В этом случае можно использовать оператор цикла по условию while.

 

 

Шаблон этого оператора следующий:

int found, sum;

 

 

while (условие) [тело цикла];

for (int i = 0; i < 10; i++){

 

 

 

if (arr[i] == 12){

 

 

Здесь условие – логическое выражение, истинное значение

 

 

которого является условием продолжения цикла, тело цикла – это

 

found++;

 

либо один оператор, либо ограниченная фигурными скобками пос-

 

continue;

 

ледовательность операторов.

}

 

 

 

Важная особенность while в том, что если к моменту его начала

}

sum += arr[i];

 

 

 

 

 

условие ложно – тело цикла не выполнится ни разу.

 

 

 

В этом примере осуществляется подсчет суммы sum всех зна-

 

Пример операторов while:

 

 

 

чений массива arr, не равных 12, и одновременно подсчет в found

while (1);

 

 

количества элементов, равных 12.

while (PORT == 2) sum++;

 

 

 

 

 

 

Первый пример демонстрирует бесконечный цикл, т.к. ненуле-

Оператор goto

вое значение равносильно истинному логическому выражению.

Последний оператор языка Си – оператор безусловного пере-

Второй пример опрашивает значение переменной PORT и ведет

хода goto. Шаблон оператора такой:

подсчет в переменной sum числа таких опросов до тех пор, пока

goto <метка>;

значение переменной равно двум12 .

Метка – это идентификатор, при объявлении завершаемый дво-

 

По сравнению с оператором for, цикл while допускает значи-

еточием. Этот идентификатор служит для обозначения определен-

тельно меньше вариантов «оформления». Очевидно, что цикл for

ного места в программе, где метка объявлена (отождествляется с

вполне в состоянии заменить цикл while:

адресом исполняемого кода в памяти программ). При использова-

 

 

 

нии в операторе goto метка указывается уже без двоеточия.

// эквивалент бесконечного цикла

 

 

Оператор goto вызывает безусловное продолжение исполне-

for (;;);

 

 

ния программы с указанной метки.

// эквивалент опроса и подсчета числа этих опросов

 

Пример использования меток и оператора goto:

for (; PORT == 2; sum++);

 

 

 

 

 

 

 

 

 

if (a < 5) goto m1;

 

 

Тем не менее, рекомендуется использовать while там, где это

оправдано логикой алгоритма, это позволит улучшить «читабель-

 

a = 0;

 

ность» программы.

 

goto m2;

 

 

 

 

m1:

 

 

Оператор do

 

a = 25;

 

 

m2:

 

 

Как было показано, тело оператора while может быть не вы-

 

 

 

 

полнено ни разу, если условие его продолжения сразу ложно. Но

Этот пример показывает реализацию следующего алгоритма:

иногда требуется, чтобы при прочих равных условиях тело цикла

если значение переменной a меньше пяти, то присвоить перемен-

всегда выполнялось хотя бы один раз. Для этих целей применяется

ной значение 25, в противном случае обнулить переменную a.

оператор do, записываемый по следующему шаблону:

Использование оператора goto в программах считается дур-

 

do [тело цикла] while (условие);

ным тоном среди программистов. Как правило, программы с эти-

 

Здесь, как и ранее, тело цикла – это либо один оператор, либо

ми операторами более запутаны, труднее отлаживаются, таят боль-

заключенная в фигурные скобки последовательность операторов,

ше потенциальных возможностей для ошибок. Метка может нахо-

а условие – это логическое выражение, определяющее условие

диться почти в любом13 месте программы, и соответственно, опе-

продолжения итераций цикла. Как и ранее, тело цикла может отсут-

ратор goto может заставить программу изменить нормальный ход

ствовать.

непредсказуемым образом, если программист случайно забудет

 

В остальном цикл do аналогичен циклу while.

вовремя убрать или изменить нужную метку. Доказано, что любой

 

 

 

алгоритм может быть реализован без использования goto, лишь с

 

Оператор break

помощью других операторов языка Си. Например, только что рас-

 

Оператор break служит для досрочного завершения операто-

смотренный алгоритм элементарно и гораздо красивее реализу-

ров цикла (любых) и оператора switch. Исполнение этого операто-

ется так:

ра равносильно переходу к оператору, следующему за «телом» упо-

 

 

 

 

if (a < 5)

 

мянутых операторов.

 

 

Пример:

 

a = 25;

 

 

 

 

else

 

int arr[10];

 

 

a = 0;

 

int found=-1;

 

 

 

 

 

for (int i = 0; i < 10; i++) if (arr[i] == 12) { found = i; break;}

 

 

 

 

 

 

В этом примере осуществляется поиск в массиве arr элемента,

 

 

 

 

равного числу 12. Как только такой элемент будет найден, цикл

 

 

 

 

завершится досрочно, и значение i будет сохранено в переменной

 

 

 

 

found. Если после завершения цикла значение found будет равно

 

 

 

 

-1, это будет означать, что элементов 12 в массиве нет, в противном

 

 

Продолжение в №3/2010

 

случае found будет равно индексу первого найденного элемента.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

12 То, каким образом значение переменной PORT может измениться – не рассматри-

13 Об ограничениях на размещение меток см. главу «Структура программы».

вается в данном контексте, предполагается пока, что все-таки такое изменение возможно.

 

 

 

 

 

 

 

6 1

Радиолюбитель – 02/2010

 

 

 

 

 

Соседние файлы в папке Книга по работе с WinAVR и AVR Studio