Приоритеты операций и порядок вычислений



В языке Си операции с высшими приоритетами вычисляются первыми. Наивысшим является приоритет, равный 1. Приоритеты и порядок операций приведены в табл. 6.

Табл. 6 – Приоритеты операций и порядок вычислений

Приоритет Типы операции Знак операции Порядок выполнения
1 Выражение 0 [] -> Справа налево
2 Унарные ~! * & ++ sizeof type (приведение типов) Слева направо
3 Мультипликативные * / % Слева направо
4 Аддитивные + Слева направо
5 Сдвиг « » Слева направо
6 Отношение < > <= >= Слева направо
7 Отношение (равенство) != Слева направо
8 Побитное И & Слева направо
9 Побитное исключающее ИЛИ   Слева направо
10 Побитное ИЛИ 1 Слева направо
11 Логическое И && Слева направо
12 Логическое ИЛИ II Слева направо
13 Условная   Слева направо
14 Простое и составное присваивание * — /= %= += &= 1= »= «= Справа налево
15 Последовательное вычисление   Слева направо

 

Пример:

/* Приоритеты операций и порядок вычислений */

unsigned char х= 0b11110000; Объявляем и инициализируем

// беззнаковый байт х unsigned char у = Ob00111100;

// Объявляем беззнаковый байт у

у&=~х|ОхАА;

/* Первой выполняется операция побитового логического отрицания (~) как имеющая самый высокий приоритет. После её выполнения все 1 в переменной х заменятся на 0, а 0 - на 1 (см. 9.1 Унарные операции). Затем выполняется операция побитного ИЛИ (I) полученного значения (Ob00001111) с числом ОхАА=Оb10101010. При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата устанавливается в 1, в противном случае - в 0 (см. Бинарные операции). Последней выполняется операция побитного ИЛИ с присваиванием (&=) значения у и результата предыдущих операций (Оb10101111). При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если оба сравниваемых бита 1, то соответствующий бит результата устанавливается в 1, в противном случае - в 0 (см. Бинарные операции). Полученное значение (Оb00101100) присваивается переменной у. Результат: у=0b00101100 */

Операторы

Порядок выполнения программы задают управляющие операторы.

Все операторы языка Си, кроме составных операторов, заканчиваются точкой с запятой (;), но в CodeVisionAVR допускается и после составного оператора ставить точку с запятой.

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

Все операторы языка Си можно условно разделить на несколько категорий (см. табл. 7)

Табл. 7Операторы языка Си

Категория Оператор

Условные операторы

if— else
switch

Операторы цикла

for
while
do while

Операторы перехода

break
continue
return
goto

Другие операторы

Оператор-выражение
Пустой оператор
Составной оператор

If-else

Оператор if-else используется при необходимости сделать выбор.

Синтаксис:

if (выражение)

[группа операторов 1] else

[группа операторов 2]

Часть с else является необязательной. Сначала вычисляется выражение; если оно истинно (т. е. значение выражения отлично от нуля), то выполняется группа операторов 1. Если оно ложно (значение выражения равно нулю) и если есть часть с else, то группа операторов 1 пропускается и выполняется группа операторов 2. Если часть с else отсутствует, то выполняется следующий за if оператор программы.

Пример:

/* Оператор if-else */

/* Переменной с будет присвоено значение переменной, наибольшей из а и b */

if (а > b)          // Если а > b,

с = а,-              // то с = а,

else                 // иначе,

с = b;               // с = b

Switch

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

Синтаксис:

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

[объявление]

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

[default: [группа операторов ]]

}

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

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

Если ни один из вариантов не подходит, то выполняется оператор, стоящий после default. Префикс default является необязательным; если его нет и ни один из случаев не подходит, то управление передаётся на следующий после switch оператор. Варианты (case) и выбор по умолчанию (default) могут располагаться в любом порядке.

После выполнения операторов, соответствующих выбранному варианту, будут выполняться операторы, соответствующие следующему варианту. Для выхода из оператора switch используется оператор break.

Пример:

/* Оператор switch */

int х=2; // Объявляем и инициализируем целую переменную у

switch (х) // Оператор switch

{

case 0: x=x-2           // Тело оператора switch
case 3: x=x-2           //  
case 2: x=x*5          //  
case 5: x=x/2 break; // Строка с оператором break
case 4: x=x+l        //  
default ;        //  

}

[группа операторов]

/* Выполнение оператора switch начинается с оператора, помеченного case 2, т. к. х=2. Таким образом, переменная х получает значение х=2*5=10. Затем выполняется оператор, помеченный ключевым словом case 5 (по порядку), х получает значение х=10/2=5. Далее по оператору break происходит выход из оператора switch на [группу операторов]. Если бы break не было, то еще выполнялись бы операторы, помеченные case 4 и default, а уже затем [группа операторов] */

Оператор for

Оператор for — это наиболее общий способ организации цикла. Он имеет следующий формат: for (выражение1; выражение2; выражение З) тело цикла.

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

При выполнении оператора for сначала вычисляется выражение 1, затем выражение 2. Если значение выражения 2 отлично от нуля (истина), выполняется тело цикла, вычисляется выражение З, снова вычисляется выражение 2, и если оно по-прежнему отлично от нуля, то цикл повторяется. Если выражение 2 равно нулю (ложь), то управление передаётся на оператор, следующий за оператором for.

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

Пример:

/* Оператор for */

/* В этом примере вычисляется сумма чисел от 1 до 10 */

void main(void)

{

int x;                            // Объявляем целую переменную х

int у=0;                       // Объявляем и инициализируем целую переменную у

int z=10;                     // Объявляем и инициализируем целую переменную z

for (х=1; x<=z; х=х+1) // Оператор цикла

 {

у=у+х;                         // Тело цикла

}

}

/* Тело цикла (у=у+1) будет выполняться, начиная со значения х=1. После каждого выполнения цикла х будет увеличиваться на 1 и тело цикла будет выполняться с новым значением х. Цикл будет выполняться до тех пор, пока х не превысит значение z, т. е. 10. Обратите внимание, что после выполнения цикла for значение х будет равно 11. Значение у будет равно 55 */

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

Пример:

/* Бесконечный цикл с оператором for */

for (;;)

{

... break;

}

While

Оператор цикла while называется циклом с предусловием.

Синтаксис:

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

В качестве выражения допускается использовать любое выражение языка Си, а в качестве тела цикла — любой оператор, в том числе пустой или составной.

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

Оператор цикла вида

for (выражение1; выражение2; выражениеЗ) тело цикла;

может быть заменён оператором while следующим образом:

выражение 1;

while (выражение 2)

{

тело

выражение З;

}

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

Пример:

/* Оператор while */

/* В этом примере вычисляется факториал числа 5 */

void main(void) {

int x=1 // Объявляем и инициализируем целую переменную х

int y=l // Объявляем и инициализируем целую переменную у

int z=5 // Объявляем и инициализируем целую переменную z

while { // Оператор цикла

/* Тело цикла while */

у=у*х; // вычисляем значение у

х=++х; // инкрементируем (увеличиваем на 1) значение х

/* Тело цикла (у=у*х, х=++х) будет выполняться, начиная со значения х=1. После каждого выполнения цикла х будет увеличиваться на 1, и тело цикла будет выполняться с новым значением х. Цикл будет выполняться до тех пор, пока х не превысит значение z, т.е. 5. Обратите внимание, что после выполнения цикла while значение х будет равно 6. Значение у будет равно 120 */

Do-while

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

Синтаксис:

do тело while (выражение).

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

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

Пример:

/* Оператор do-while */

/* В этом примере вычисляется факториал числа 5 */

void main(void) {

int x=l; // Объявляем и инициализируем целую переменную х

int у=1; // Объявляем и инициализируем целую переменную у

int z=5; // Объявляем и инициализируем целую переменную z

do // Оператор цикла

/* Тело цикла do*/

у=у*х; // вычисляем значение у

х=++х; // инкрементируем (увеличиваем на 1) значение х

/*while */

} while (x<=z); // проверка условия

/* Тело цикла (у=у*х, х=++х) будет выполняться, начиная со значения х=1. После каждого выполнения цикла х будет увеличиваться на 1, затем будет проверяться условие (x<=z), и, если оно истинно, тело цикла будет повторяться с новым значением х. Цикл будет выполняться до тех пор, пока х не превысит значение z, т. е. 5. Обратите внимание, что после выполнения цикла do-while значение х будет равно 6. Значение у будет равно 120 */

Break

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

Пример:

/* Оператор break */

/* Поиск в заданном массиве элемента, равного 0 */

/* Подключим заголовочный файл со стандартными функциями ввода/вывода */

#include <stdio.h>               // подключаем заголовочный файл со стандартными функциями ввода/вывода

int array[7]={-3,-2,-1,0,1,2,3}; // Объявляем и инициализируем целочисленный массив array

int n=7;                          // Объявляем и инициализируем целую переменную п - количество элементов массива

void main(void){        // Основная функция программы

int i;                          // Объявляем целую переменную i, которая будет задавать индекс элемента массива array

for(i=0;i<n;i++)        // Цикл for

/* Тело цикла for */

 if(array[i]==0) break;    // Если i-й элемент массива равен 0, то выход из цикла for по break

/* Оператор if-else */

if(i==n)                        // Если i=n,

printf("%d not found \n",0); // то выводим строку: "0 not found "

else                                         // Если i не равен n,

printf("%d on %d place \n",0,i); // то выводим строку: "0 on i place ", где i — номер места

/* Тело цикла (if(array[i]=0) break) будет выполняться, начиная со значения i=0. После каждого выполнения цикла i будет увеличиваться на 1, затем будет проверяться условие (i<n), и, если оно истинно, тело цикла будет повторяться с новым значением i. Цикл будет выполняться до тех пор, пока i не станет равно п или выражение в теле цикла for не станет истиной, и будет осуществлён выход из цикла по оператору break. В обоих случаях будет осуществлён переход на оператор if-else. */

Continue

Оператор continue прерывает выполнение цикла, но в отличие от оператора break он приводит к началу следующей итерации охватывающего цикла (do-while, for, while). В циклах while и do-while это означает непосредственный переход к выполнению проверочной части; в цикле for управление передаётся на шаг изменения параметра цикла.

Пример:

/* Оператор continue */

/* В этом фрагменте будут обрабатываться только положительные элементы массива array */

for (х=0; х<п; х++)               // Цикл for

/* Здесь производится обработка положительных элементов массива */

[группа операторов]

}

/* х - индекс элемента массива array[х]. Выполнение цикла for начинается с х=0. Проверяется элемент array[0] массива, и если он меньше 0, то по оператору continue осуществляется переход к следующей итерации цикла for, т. е. х увеличивается на 1 (х++) и проверяется элемент array[1], и т. д. Если элемент массива array[х] больше 0, то оператор continue пропускается и выполняется [группа операторов], которая обрабатывает положительные элементы массива. После выполнения [группы операторов] также осуществляется переход к следующей итерации цикла for и цикл снова повторяется с х=х+1.

Цикл for будет выполняться, пока значение х не станет равно (или больше) значению п (условие х<п) */

/* Тело цикла for */

if (array[х] < 0) continue;     // Если элемент массива меньше 0, то следующую часть тела цикла пропускаем и переходим к следующей итерации цикла for

Return

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

Синтаксис:

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

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

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

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

Пример:

/* Оператор return */

/* Функция summa */

int summa (int x, int y)

{

return (x+y);   // Зададим возвращаемое значение

}                

/* Основная функция программы */

void main(void) {

int a=5, b=10; // Объявляем и инициализируем целые переменные а и b

/* Функция summa имеет два формальных параметра - х и у типа int, и возвращает значение типа int, о чем говорит описатель, стоящий перед именем функции. После вызова функции summa в основной программе оператор return вернёт значение, равное сумме фактических параметров а и b, т. е. с=а+b=5+10=15 */

int c;                // Объявляем целую переменную с

с = summa (а, b);      // Вычислим значение с через вызов функции summa

Пример:

/* Оператор return */

#include <stdio.h>// подключаем заголовочный файл со стандартными функциями ввода/вывода

/* Функция print_pozitiv*/

 void print_pozitiv (int a) {

if (a<0) return; // Если фактический аргумент отрицательный, то возвращаемся

else                             // Если фактический аргумент положительный или равен 0, выводим его значение и возвращаемся

printf ( return; %d \n", a);

/* Основная функция программы */

void main(void)

{

int x=5;            // Объявляем и инициализируем целую переменную х

 print_pozitiv (х);       // Вызовем функцию print_pozitiv с фактическим аргументом х=5

[группа операторов]

}

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

В основной программе присваивается значение фактическому аргументу и вызывается функция print_pozitiv. Функция print_pozitiv проверяет значение аргумента, и если оно отрицательное, то по оператору return осуществляется возврат в основную функцию на [группу операторов]. Если значение аргумента неотрицательное, то выводится его значение и также, по оператору return, осуществляется возврат в основную функцию на [группу операторов]. Наличие второго оператора return необязательно */

 

Оператор return также можно использовать для выхода из оператора switch.

Пример:

/* Оператор return */

int function(int n, int x) {     // определение функции function

switch (n) {                            // Оператор switch 

case 0: return (1);                 // Если значение фактического аргумента, соответствующего формальному аргументу n, равно 0, то функция вернёт значение 1.

case 1: return (2) ;    // Если равно 1 - функция вернёт 2.

case 2: return (x) ;    // Если 2 - функция вернёт значение фактического аргумента, соответствующего формальному аргументу х.

case 3: return (х-2); // Если 3 - функция вернёт х-2.

default: return (0);    // Во всех других случаях функция вернёт значение 0.

Goto

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

Синтаксис: goto имя-метки;

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

С формальной точки зрения оператор goto никогда не является необходимым, и на практике почти всегда можно обойтись без него. Тем не менее, есть несколько ситуаций, где оператор goto может найти своё место. Наиболее характерным является его использование тогда, когда при выполнении какого-то условия требуется прервать выполнение в некоторой глубоко вложенной структуре, например, выйти сразу из двух циклов. Здесь нельзя непосредственно использовать оператор break, так как он прерывает только самый внутренний цикл.

Пример:

/* Оператор goto */

for (...)                         // Первый цикл

for (...)                       // Второй цикл

if (...) goto Label;                   // Если условие оператора if выполняется, то переход на метку Label

Label:                         // метка Label, после которой следуют операторы

[группа операторов]

 

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

Использование оператора безусловного перехода goto в практике программирования на языке Си настоятельно НЕ РЕКОМЕНДУЕТСЯ, так как он затрудняет понимание программ и возможность их модификаций.

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

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

Выполнение оператора-выражения заключается в вычислении выражения. Полученное значение выражения никак не используется.

Пример:

/* Оператор — выражение */

++х;     // Этот оператор-выражение увеличивает значение переменной х на единицу

а=5;     // Этот оператор-выражение присваивает переменной а значение 5

y=function(х); // Этот оператор-выражение включает в себя операции присваивания (у=...) и вызова функции (function(х))

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

Пустой оператор состоит только из точки с запятой. При выполнении этого оператора ничего не происходит. Синтаксис: ;

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

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

Пример:

/* Пустой оператор */

int function ( ) // определение функции function

{

{

if (...)                // Если условие оператора if выполняется,

goto Label;                 // то осуществляется переход на метку Label, т. е. на скобку

{

Label: ; }         // метка Label с пустым оператором, после которой следует скобка

return (...) ;

 

Пример:

/* Пустой оператор */

/* Организация бесконечного цикла с помощью пустого оператора */

while (1)         // Условие в скобках всегда истинно

/* Тело цикла while */

;                        // Пустой оператор

Составной оператор

Составной оператор представляет собой несколько операторов и объявлений. Для объединения объявлений и операторов в составной оператор или блок используются фигурные скобки ({ }). Одним из примеров такого типа являются фигурные скобки, в которые заключаются операторы, составляющие функцию.

Синтаксис:

{[объявление] оператор; [оператор];}

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

Пример:

/* Составной оператор */

/* Пример полного определения функции, которая возвращает максимальное из трех значений переменных */

int maximum(int a, int b, int с) // Объявление функции maximum, которая возвращает целое значение, т. к. перед именем функции стоит int

После первой фигурной скобки, которая завершает блок (составной оператор), в языке Си точка с запятой НИКОГДА не ставится. В Code VtsionA VR ДОПУСКАЕТСЯ ставить точку с запятой.

/* Составной оператор */

{

int d;                            // Объявление целой переменной d

d = (а>b)? a : b; return!(d>c)? d : c); // Переменной d присваивается максимальное из значений переменных а и b. Функция возвращает максимальное из значений переменных d и с (Тернарные операции)

}

1.11 Функции

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

Функция — это совокупность объявлений и операторов, обычно предназначенная для решения определённой задачи. С использованием функций в языке Си связаны три понятия: определение функции (описание действий, выполняемых функцией), объявление функции (задание формы обращения к функции) и вызов функции.

Каждая функция должна иметь имя, которое используется для её объявления, определения и вызова.

В любой программе должна быть функция с именем main (главная функция), и именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы.

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

Синтаксис для объявления функции:

[<класс памяти>] [<тип>]имя_функции ( [<список параметров;»] {

тело функции (список операторов)

}

Необязательный спецификатор класса памяти задаёт класс памяти функции, который может быть static или extern (подробнее о классах памяти см. Переменные).

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

Функция не может возвращать массив или функцию, но может возвращать указатель на любой тип, в том числе и на массив, и на функцию.

Список формальных параметров — это последовательность объявлений формальных параметров, разделённая запятыми. Формальные параметры — это пе­ременные, используемые внутри тела функции и получающие значение при вызове функции путем копирования в них значений соответствующих фактических параметров. Список формальных параметров может заканчиваться запятой (,) или запятой с многоточием (,...); это означает, что число аргументов функции переменно. Однако предполагается, что функция имеет, по крайней мере, столько обязательных аргументов, сколько формальных параметров задано перед последней запятой в списке параметров. Такой функции может быть передано большее число аргументов, но над дополнительными аргументами не проводится контроль типов. Если тип формального параметра не указан, то этому параметру присваивается тип int.

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

Тело функции — это составной оператор, содержащий операторы, определяю­щие действие функции.

Объявление функции имеет такой же вид, что и определение функции, с той лишь разницей, что тело функции отсутствует и имена формальных параметров тоже могут быть опущены. Чтобы объявить функцию, можно использовать функциональные прототипы. Прототип —это явное объявление функции, которое предшествует её определению. Тип возвращаемого значения при объявлении функции должен соответствовать типу возвращаемого значения в определении функции.

Пример:

/* Прототип функции */

int function(int a, int b, int с)          // Объявление функции function, которая возвращает целое значение, т. к. перед именем функции стоит int

/* Фактическое определение функции может быть написано где-нибудь в другом месте программы */

int function(int a, int b, int с) // тело функции

{

[группа операторов]

}

При вызове функции ей при помощи аргументов (формальных параметров) могут быть переданы некоторые значения (фактические параметры), используемые во время выполнения функции. Функция может возвращать некоторое (но ТОЛЬКО ОДНО) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов ни встретился. Допускается также использовать функции, не имеющие аргументов, и функции, не возвращающие никаких значений. Действие таких функций может состоять, например, в изменении значений некоторых переменных, выводе некоторых значений и т.п.

Пример:

/* Вызов функции */

/* определение функции с формальными параметрами а, b, и с */

int function(int a, int b, int с)

{

return (a+b+c);          // тело функции

}

 

/* Основная функция программы */

void main(void)

{

int x=5, y=6, z=10;                // Объявляем и инициализируем целые переменные х, у и z int result; Объявляем целую переменную result

result=function(x,y,z); // Вызовем функцию function с фактическими параметрами х,у и z. В результате функция вернёт значение суммы этих параметров: result=5+6+10=21

}

 

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

Пример:

/* определение функции в "старом стиле", которое CodeVisionAVR не поддерживает. Будет выдаваться синтаксическая ошибка!!! */

int maximum(a, b, с) int a, b, с;                  // тело функции

{

[группа операторов]      

/* Правильное определение функции */

int function(int a, int b, int с)          // тело функции

{

[группа операторов]       

}

Параметры функций передаются через Data Stack (Стек данных). Значения функций возвращаются в регистры R30, R31, R22 и R23 (от младшего байта (LSB) к старшему (MSB)).

Указатели

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

Синтаксис:

[ кодификатор места хранения переменной>] type [«модификатор места хранения указателя;»] имя_указателя;

или

type [кодификатор места хранения переменной>]*[кодификатор места хранения указателя>] имя_указателя;

где type может быть любым типом данных (int, char, struct и т. д.).

Пример:

unsigned int * а;       // переменная а представляет собой указатель на переменную типа unsigned int (целое без знака)

char * х;          // переменная х представляет собой указатель на переменную типа char

int nomer;                  // Объявляем целую переменную nomer

int *addres;                // Переменную addres объявляем как указатель,

addres = & nomer;    // ей присваиваем адрес переменной nomer (& - операция вычисления адреса)

int х,у;             // Объявляем целые переменные х и у

int *point_x;               // Переменная point_x объявлена как указатель,

point_x = &х; // ей присваивается адрес переменной х,

у = *point_x;   // Переменной у присваивается значение х, это равносильно оператору: у = х;

 

Из-за Гарвардской архитектуры микроконтроллеров AVR, с раздельным пространством адресов для данных SRAM, программы FLASH- и EEPROM-памяти, компилятор CodeVisionAVR имеет три типа указателей.

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

Для доступа к константам, расположенным во FLASH-памяти, используется модификатор типа места хранения flash.

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

Хотя указатели могут указывать на различные области памяти, по умолчанию они хранятся в SRAM.

Пример:

/* Указатель на символьную строку, расположенную в SRAM */

char *point_to_sram = "String in SRAM";

/* Указатель на символьную строку, расположенную во FLASH */

flash char *point_to_flashl = "Stringl in FLASH";

char flash *point_to_flash2 = "String2 also in FLASH";

/* Указатель на символьную строку, расположенную в EEPROM */

eeprom char *point_to_eepl = "Stringl in EEPROM";

char eeprom *point_to_eep2 = "String2 also in EEPROM";

 

Для того чтобы сохранить сам указатель в других областях памяти, типа FLASH или EEPROM, должны быть использованы модификаторы хранения указателя flashили eeprom.

Пример:

/* Указатель - во FLASH, символьная строка - в SRAM */

char *flash flash_point_to_sram = "String in SRAM";

/* Указатель - во FLASH, символьная строка - во FLASH */

 flash char *flash flash_point_to_flash = "String in FLASH";

/* Указатель - во FLASH, символьная строка - в EEPROM */

eeprom char *flash flash_point_to_eep = "String in EEPROM";

/* Указатель - в EEPROM, символьная строка - в SRAM */

char *eeprom eep_point_to_sram = "String in SRAM";

/* Указатель - в EEPROM, символьная строка - во FLASH */

flash char *eeprom eep_point_to_flash = "String in FLASH";

/* Указатель - в EEPROM, символьная строка - в EEPROM */

eeprom char *eeprom eep_point_to_eep = "String in EEPROM";

 

Указатели на области памяти FLASH и EEPROM всегда используют 16 битов.

Поскольку указатели во FLASH-памяти используют 16 битов, общий размер массива констант и символьных строк не может превышать 64 К для ATmegal03 или ATmegal28. Тем не менее общий размер программы для этих чипов может быть 128 К.

Указатели могут быть сгруппированы в массивы, которые могут иметь до 8 измерений.

Пример:

/* Глобальные массивы указателей */

/* Объявим и инициализируем глобальный массив указателей на символьные строки, размещённые в SRAM. Сам массив указателей - в SRAM */

char *array_point[4] = {"Str_sraml","Str_sram2","Str_sram3","Str_sram4"};

/* Объявим и инициализируем глобальный массив указателей на символьные строки, размещённые во FLASH. Сам массив указателей - во FLASH */

flash char *flash array_point_flash[2] = {"Str_flashl","Str_flash2"};

/* Объявим и инициализируем глобальный массив указателей на символьные строки, размещенные в EEPROM. Сам массив указателей - в EEPROM */

eeprom char *eeprom array_point_eep[3] = {"Str_eepl","Str_eep2","Str_eep3"}

 

Пример:

/* Локальные массивы указателей */

/* Объявим и инициализируем несколько строк в EEPROM */

eeprom char strl[]="Stringl";

eeprom char str2[]="String2";

 eeprom char str3[]="String3";

/* Основная функция программы */

void main(void) {

/* Объявим локальный массив указателей на символьные строки, размещённые в EEPROM. Сам массив указателей - в SRAM */

char eeprom *local_array_point[3];

/* и инициализируем массив */

local_array_point[0] = strl;

 local_array_point[1] = str2;

 local_array_point[2] = str3;

}

 

Указатели функций всегда занимают 16 битов, поскольку они используются для доступа к области FLASH-памяти. Для этих типов указателей нет необходимости использовать ключевое слово flash.

Пример:

/* Указатели функций */

/* Объявим и определим функцию summa */

int summa(int x, int у, int z) {

return (x+y+z);

}

/* Объявим и инициализируем глобальный указатель функции summa */

 int (*sum__ptr) (int x, int y, int z)=summa;

/* Основная функция программы */

void main(void) {

int i;

i=(*sum_ptr) (5,12,7);         // Вызовем функцию summa с фактическими аргументами 5, 12 и 7, используя указатель. Функция вернёт сумму этих значений. В результате i=5+12+7=24


Дата добавления: 2018-04-15; просмотров: 1376; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!