Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

691_Mikushin_A.V._Programmirovanie_mikroprotsessorov_

.pdf
Скачиваний:
48
Добавлен:
12.11.2022
Размер:
1.96 Mб
Скачать

Некоторые варианты использования оператора for повышают его гибкость за счет возможности использования нескольких переменных, управляющих циклом.

Пример:

int main() {int top,bot;

char string[100],temp; for(top=0,bot=100;top<bot;top++,bot--) {temp=string[top];

string[bot]=temp;

}

return 0;

}

В этом примере, реализующем запись строки символов в обратном порядке, для управления циклом используются две переменные top и bot. Отметим, что на месте выражение 1 и выражение 3 здесь используются несколько выражений, записанных через запятую, и выполняемых последовательно.

Так как согласно синтаксису языка С-51 оператор может быть пустым, тело оператора for также может быть пустым. Такая форма оператора может быть использована для организации поиска.

Пример:

for(i=0;t[i]<10;i++);

В данном примере переменная цикла i принимает значение номера первого элемента массива t, значение которого больше 10.

При написании программ для микроконтроллеров достаточно часто требуется реализовать бесконечный цикл. В этом случае оператор цикла for принимает следующий вид:

for(;;)

//Постоянно

{Knop=P2;

//опрашивать порт P2

ObrabSobyt(); //и обрабатывать нажатие кнопок

}

Оператор цикла for – это универсальный оператор, поэтому его реализация в машинных командах достаточно сложна. Для написания программ часто достаточно более простых операторов цикла. Использование таких операторов обычно приводит к более компактным и быстродействующим программам, однако это не относится к языку программирования C-51. В компиляторе языка программирования C-51 именно этот оператор оптимизирован наилучшим образом.

Оператор цикла с проверкой условия до тела цикла while.

Оператор цикла while называется циклом с предусловием и имеет следующий формат:

31

while (выражение) тело цикла;

Оператор while содержит условную операцию (такую же, как в операторе if), и вызывает исполнение операторов в этом блоке до тех пор, пока условие верно. Проверка условия производится до выполнения тела цикла, поэтому оператор тела цикла может быть не выполнен ни разу. В качестве выражения допускается использовать любое выражение языка C, а в качестве тела любой оператор, в том числе пустой или составной. Схема выполнения оператора while следующая:

1.Вычисляется выражение.

2.Если выражение ложно, то выполнение оператора while заканчивается и выполняется следующий по порядку оператор. Если выражение истинно, то выполняется тело цикла (которое может быть составным оператором)

3.Процесс повторяется с пункта 1.

Оператор while может быть полезен для ожидания срабатывания какоголибо устройства микроконтроллера, например таймера:

...

while(!TF0); //Программа ожидает переполнения таймера T0

TF0=0;

TL0=time; //Настроить таймер T0 TН0=time>>8; //на очередной интервал времени

...

В следующем примере оператор while используется для пошагового прохождения по элементам массива Table до тех пор, пока очередной элемент не превысит значение скалярной переменной с именем Level (уровень англ.):

i = 0; while(table(i)<=Level)i++;

В приведённом примере Table – это предварительно объявленный массив. Переменные Level и i тоже должны быть предварительно объявлены.

Переменной i первоначально присваивается значение 0, затем она используется как индекс для массива Table. Так как i увеличивается при каждом проходе цикла while, то каждый раз, когда выполняется оператор внутри блока while, с переменной Level сравнивается следующий элемент массива Table. Когда найден элемент, превышающий значение переменной Level, то условие в операторе while больше неверно, выполнение блока не повторяется и управление передается следующему за циклом while оператору. С этого момента переменная i является индексом первого элемента массива Table, который превышает значение переменной Level.

32

Оператор цикла с проверкой условия после тела цикла do while.

Оператор цикла while называется циклом с постусловием и имеет следующий формат:

do тело цикла while (выражение);

Оператор do while содержит условную операцию (такую же, как в операторе if), и вызывает исполнение операторов в этом блоке до тех пор, пока условие верно. Проверка условия производится после выполнения тела цикла, поэтому оператор тела цикла будет выполнен хотя бы один раз. В качестве выражения допускается использовать любое выражение языка Си, а в качестве тела любой оператор, в том числе пустой или составной. Схема выполнения оператора do while следующая:

1.Выполняется тело цикла (которое может быть составным оператором)

2.Вычисляется выражение.

3.Если выражение ложно, то выполнение оператора do while заканчивается и выполняется следующий по порядку оператор. Если выражение истинно, то выполнение оператора продолжается с пункта 1.

Чтобы прервать выполнение цикла до того, как условие станет ложным, можно использовать оператор break.

Оператор do while в большинстве случаев соответствует одной машинной команде, поэтому может быть использован для написания эффективных по коду и быстродействию программ. Например:

i=10

do тело цикла; while(--i<0);

Однако, конкретно в реализации компилятора C-51, намного эффективнее следующая конструкция:

for(i=10;i>0;i--) тело цикла;

Операторы while и do while могут быть вложенными. Пример использования вложенных операторов цикла:

int i,j,k;

...

i=0;j=0;k=0; do{i++;j--;

while (a[k]<i)k++;

}

while(i<30&&j<-30);

Оператор break.

Оператор break обеспечивает прекращение выполнения самого внутреннего из охватывающих его операторов switch, do, for, while. После выполнения

33

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

Оператор continue.

Оператор continue, как и оператор break, используется только внутри операторов цикла, но в отличие от него выполнение цикла не прерывается, а начинается следующая итерация цикла. Формат записи оператора:

continue;

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

int main() {int a,b;

for(a=1,b=0;a<100;b+=a,a++)

{if(b%2)continue;

... /* обработка четных сумм */

}

return 0;

}

Когда сумма чисел от 1 до а становится нечетной, оператор continue передает управление на очередную итерацию цикла for, не выполняя операторы обработки четных сумм.

Оператор continue, как и оператор break, прерывает самый внутренний из объемлющих его циклов.

Оператор выбора switch.

Оператор switch предназначен для организации выбора из множества различных вариантов. Формат оператора следующий:

switch ( выражение ) {[объявление]

...

[ case константное-выражение1]: [ список-операторов1] [ case константное-выражение2]: [ список-операторов2]

...

...

[ default: [ список операторов ]]

}

Выражение, следующее за ключевым словом switch в круглых скобках, может быть любым выражением, допустимыми в языке C, значение которого должно быть целым. Отметим, что можно использовать явное приведение к целому типу.

Значение этого выражения является ключевым для выбора из нескольких вариантов. Тело оператора switch состоит из нескольких операторов, помеченных ключевым словом case с последующим константным выражением.

34

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

Все константные выражения в операторе switch должны быть уникальны. Кроме операторов, помеченных ключевым словом case, может быть, но обязательно один, фрагмент помеченный ключевым словом default.

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

Отметим также, что в операторе switch можно использовать свои локальные переменные, объявления которых находятся перед первым ключевым словом case, однако в объявлениях не должна использоваться инициализация.

Схема выполнения оператора switch следующая:

1.вычисляется выражение в круглых скобках;

2.вычисленные значения последовательно сравниваются с константными выражениями, следующими за ключевыми словами case;

3.если одно из константных выражений совпадает со значением выражения, то управление передается на оператор, помеченный соответствующим ключевым словом case;

4.если ни одно из константных выражений не равно выражению, то управление передается на оператор, помеченный ключевым словом default, а в случае его отсутствия управление передается на следующий после switch оператор.

Отметим интересную особенность использования оператора switch: конструкция со словом default может быть не последней в теле оператора switch. Ключевые слова case и default в теле оператора switch существенны только при начальной проверке, когда определяется начальная точка выполнения тела оператора switch. Все операторы, между начальным оператором и концом тела, выполняются вне зависимости от ключевых слов, если только какой-то из операторов не передаст управления из тела оператора switch. Таким образом, программист должен сам позаботится о выходе из оператора case, если это необходимо. Чаще всего для этого используется оператор break.

Для того чтобы выполнить одни и те же действия для различных значений выражения, можно пометить один и тот же оператор несколькими ключевыми словами case.

Если значение выражения равно 0, то выполняется только первый оператор присваивания, и 0 будет присвоен переменной red (красный англ.). Если значение выражения равно 1, то будет выполнен только второй оператор присваивания и переменной blue (голубой англ.) будет присвоен 0. Значения выражения 2 и 3 вызовет присваивание 0 переменным green (зеленый англ.) и gray (серый англ.) соответственно.

Следует отметить, что с точки зрения программиста этот оператор выглядит очень эффектно. Однако при рассмотрении машинного кода получается довольно страшная конструкция с использованием таблиц переходов. Использо-

35

вание нескольких условных операторов if несмотря на то, что они не очень красиво выглядят в исходном тексте программы, приводит к короткому и быстродействующему машинному коду программы.

Оператор безусловного перехода goto.

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

goto имя-метки;

...

имя-метки: оператор;

Оператор goto передает управление на оператор, помеченный меткой имяметки. Помеченный оператор должен находиться в той же функции, что и оператор goto, а используемая метка должна быть уникальной, т.е. одно имя-метки не может быть использовано для разных операторов программы. Имя-метки – это идентификатор.

Любой оператор в составном операторе может иметь свою метку. Используя оператор goto, можно передавать управление внутрь составного оператора. Но нужно быть осторожным при входе в составной оператор, содержащий объявления переменных с инициализацией, так как объявления располагаются перед выполняемыми операторами и значения объявленных переменных при таком переходе будут не определены.

Использование операторов goto является неизбежным при некоторых ситуациях, однако в большинстве случаев там, где должна быть предусмотрена передача управления, более предпочтительным является использование итеративного оператора while, do while, switch, if или вызова функции. Неограниченное использование операторов goto в программе приводит к тому, что программу становится трудно понимать, модифицировать и сопровождать. Реальная практика использования языка C-51 показывает, что в большинстве случаев программные модули могут не содержать оператор goto без ухудшения их эффективности.

Оператор выражение.

Любое выражение, которое заканчивается точкой с запятой, является оператором.

Выполнение оператора выражение заключается в вычислении выражения. Полученное значение выражения никак не используется, поэтому, как правило, такие выражения вызывают побочные эффекты. Заметим, что вызвать подпро- грамму-процедуру, можно только при помощи оператора выражения.

Примеры записи операторов-выражений:

++ i; //Этот оператор представляет выражение, которое увеличивает значение переменной i

//на единицу.

36

a(x,y); //Этот оператор представляет выражение состоящее из вызова подпро- граммы-процедуры.

Оператор возвращения из подпрограммы return.

Оператор return завершает выполнение функции, в которой он задан, и возвращает управление в вызывающую функцию, в точку, непосредственно следующую за вызовом. Формат оператора:

return [выражение] ;

Значение выражения, если оно задано, возвращается в вызывающую функцию в качестве значения вызываемой функции. Если выражение опущено, то возвращаемое значение не определено. Выражение может быть заключено в круглые скобки, хотя их наличие не обязательно.

Если в какой-либо функции отсутствует оператор return, то передача управления в вызывающую функцию происходит после выполнения последнего оператора вызываемой функции. При этом возвращаемое значение не определено. Если функция не должна иметь возвращаемого значения (подпрограм- ма-процедура), то ее нужно объявлять с типом void.

Таким образом, использование оператора return необходимо либо для немедленного выхода из функции, либо для передачи возвращаемого значения.

Пример:

int sum(int a,int b) {return(a+b);

}

Функция sum имеет два формальных параметра a и b типа int, и возвращает значение типа int, о чем говорит описатель, стоящий перед именем функции. Возвращаемое оператором return значение равно сумме фактических параметров.

Пример:

void prov (int a, double b) {float c;

if(a<3)

return; else if(b>10)

return; else

{c=a+b; if((2*c-b)==11)return;

}

}

В этом примере оператор return используется для выхода из функции в случае выполнения одного из проверяемых условий.

37

Пустой оператор.

Пустой оператор состоит только из точки с запятой. У пустого оператора может быть метка. Пустой оператор не содержит ничего, кроме пробелов и комментариев. При выполнении этого оператора ничего не происходит. Он обычно используется в следующих случаях:

1.в операторах цикла do while, for, while при использовании их в качестве элементов задержки, в операторе if в строках, когда место оператора не требуется, но по синтаксису требуется хотя бы один оператор;

2.при необходимости пометить структурный оператор.

Синтаксис языка С-51 требует, чтобы после метки обязательно следовал оператор. Фигурная же скобка оператором не является. Поэтому, если надо передать управление на фигурную скобку, приходится использовать пустой оператор.

Пример:

int main ( ) { ...

{if (...) goto a; /* переход на скобку */

{ ...

}

a:;} return 0;

}

1.6. Объявление переменных в языке программирования C-51

Описание переменных в языке программирования C имеет огромнейшее значение, так как именно оно в большинстве случаев определяет объем программы. Обычно большой объем загрузочного модуля программы вызван неправильным объявлением переменных в исходном тексте программы. Обращение к внутренним регистрам микроконтроллеров и внешним ресурсам разрабатываемого устройства тоже производится при помощи заранее объявленных переменных.

В языке программирования C-51 любая переменная должна быть объявлена до первого использования этой переменной. Как уже говорилось ранее, этот язык программирования предназначен для написания программ для микроконтроллеров семейства MCS-51, поэтому в составе языка должна отображаться внутренняя структура этого семейства микроконтроллеров. Эти особенности отражены во введении новых типов данных. В остальном язык программирования C-51 не отличается от стандарта ANSI.

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

[спецификатор класса памяти] спецификатор типа [спецификатор типа памя-

ти] описатель [=инициатор] [,описатель [= инициатор] ]...

38

Описатель – идентификатор простой переменной либо более сложная конструкция с квадратными скобками, круглыми скобками или звездочкой (набором звездочек).

Спецификатор типа – одно или несколько ключевых слов, определяющих тип объявляемой переменной. В языке C-51 имеется стандартный набор типов данных, используя который можно сконструировать новые (уникальные) типы данных.

Инициатор задает начальное значение или список начальных значений, которые (которое) присваивается переменной при объявлении.

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

слов языка C: auto, bit, extern, register, sbit, sfr, sfr16 static auto,

extern, register, static, и указывает, каким образом будет распределяться память под объявляемую переменную с одной стороны, а с другой, область видимости этой переменной, т.е., из каких частей программы можно к ней обратиться.

Спецификатор типа памяти определяется одним из шести ключевых слов

языка С-51: code, data, idata, bdata, xdata, pdata и указывает, в какой обла-

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

Категории типов данных.

Ключевые слова для определения основных типов данных:

Целые типы данных: Типы данных с плавающей запятой: bit float

sbit char int short long signed unsigned sfr sfr16

Переменная любого типа может быть объявлена как неизменяемая. Это достигается добавлением ключевого слова const к спецификатору типа. Объекты с типом const представляют собой данные, используемые только для чтения, т.е. этой переменной не может быть присвоено новое значение. Отметим, что если после слова const отсутствует спецификатор типа, то подразумевается спецификатор типа int. Если ключевое слово const стоит перед объявлением составных типов (массив, структура, смесь, перечисление), то это приводит к тому, что каждый элемент также должен являться немодифицируемым, т.е. значение ему может быть присвоено только один раз.

Примеры использования ключевого слова const:

const float A=2.128E-2;

const B=286; //подразумевается const int B=286

39

Отметим, что переменные со спецификатором класса памяти размещаются во внутреннем ОЗУ. Неизменяемость контролируется только на этапе трансляции. Для размещения переменной в ПЗУ лучше воспользоваться спецификатором типа памяти code

Целые типы данных.

Для определения данных целого типа используются различные ключевые слова, которые определяют диапазон значений и размер области памяти, выделяемой под переменные (табл. 7).

Отметим, что ключевые слова signed и unsigned необязательны. Они указывают, как интерпретируется нулевой бит объявляемой переменной, т.е., если указано ключевое слово unsigned, то нулевой бит интерпретируется как часть числа, в противном случае нулевой бит интерпретируется как знаковый.

В случае отсутствия ключевого слова unsigned целая переменная считается знаковой. В том случае, если спецификатор типа состоит из ключевого типа signed или unsigned и далее следует идентификатор переменной, то она будет рассматриваться как переменная типа int. Например:

unsigned int n;

//Беззнаковое шестнадцатиразрядное число n

unsigned int b;

 

 

 

int c;

//подразумевается

signed

int c

unsigned d;

//подразумевается

unsigned int d

signed f;

//подразумевается

signed

int f

 

 

 

Таблица 7

Тип

Размер памяти

Размер памяти

Диапазон значений

 

в битах

в байтах

 

bit

1

 

от 0 до 1

 

 

 

 

char

8

1

от –128 до 127

unsigned shar

8

1

oт 0 до 255

 

 

 

 

int, short

16

2

от –32768 до 32767

long

32

4

от –2 147 483 648 до

 

 

 

2 147 483 647

unsigned int,

16

2

от 0 до 65535

unsigned short

 

 

 

 

 

 

 

unsigned long

32

4

от 0 до 4 294 967 295

sbit

1

 

0 или 1

 

 

 

 

sfr

8

1

oт 0 до 255

sfr16

16

2

от 0 до 65535

Отметим, что модификатор типа char используется для представления одиночного символа или для объявления строковых литералов. Значением объекта типа char является код (размером 1 байт), соответствующий представляемому символу.

40