Домашняя страница библиотеки_c AVR

Синтаксис языка C

Библиотека языка C GNU glibc

 Страницы развития библиотеки_с AVR

Главная страница

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

Содержание библиотеки_c

Часто задаваемые вопросы

Содержание по алфавиту

Демонстрационные проекты


 

Функция – подпрограмма, часть программного кода, исполняемого в стеке.

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

При этом используется следующий синтаксис:

Тип_возвращаемого_значения  Имя_функции (Список_параметров)

{

//Тело  функции

}

ПРИМЕЧАНИЕ

Предварительное объявление функции может отсутствовать. В этом случае она доступна только внутри того файла, в котором определена.

 

Список параметров. В функцию могут передаваться параметры, один или несколько — это идентификаторы, которые могут использоваться внутри функции. Вместо них подставляются соответствующие значения, указанные при вызове функции (если в функцию передается более одного параметра, то они от­деляются друг от друга запятыми, как при объявлении, так и при вызове).

Значения, переданные в функцию, фактически не изменяются, а просто копи­руются в параметры, которые в этом смысле выполняют роль локальных пере­менных. При этом следует следить за тем, чтобы тип передаваемых значений со­ответствовал типу параметров, объявленных в заголовке функции.

 

Возвращаемые значения. Возвращать функция может только один параметр.

ПРИМЕЧАНИЕ

Используя указатели можно организовать возвращение более одного параметра.

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

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

Процедура – функция, не возвращающая никаких значений. В приведённом в первой главе примере «SOS» все функции являются процедурами.

 

Функцию вызывают по ее имени с указанием в круглых скобках перечня передаваемых параметров (если их нет, то в скобках ничего не указывается), например:

 

void Procedure(int n, char с)    /* Объявляем локальные переменные n, c (значения этих переменных передаются из вне) */

{

unsigned char i = 0;      // Объявляем локальную переменную i

}

 

int Function()

{

int h;                            // Объявляем локальную переменную h

char b;                                     // Объявляем локальную переменную b

h = (int) b * 10;

return h;

}

 

int main()

{

int x = 1000;                // Объявляем переменную x, с начальным значением 1000.

char y = 100;               // Объявляем переменную   y, с начальным значением 100.

 

            While(1)

            {

Procedure (x, y);          // Вызываем процедуру, передаём переменным значения n = x, с = y = 100.

x = Function();             // Изменяем значение x

}

}

 

Функция main. Любая программа на языке C содержит главную функцию под названием int main(). Эта функция при запуске программы выполняется первой.

 

Библиотечные функции. В языке C имеется набор стандартных функций (см. приложение), также компилятор AVR-GCC имеет набор дополнительных функций.

 

Классы памяти при объявлении локальных переменных. Локальные переменные могут быть объявлены внутри функций как принад­лежащие к одному из трех классов памяти:

auto (значение по умолчанию, можно явно не указывать) — при объявле­нии переменная не инициализируется никаким значением (значение — те­кущее содержимое области памяти, отведенной под переменную); при вы­ходе из функции переменная удаляется из памяти;

static — статическая переменная доступна только в пределах функции, хотя память для нее выделяется в пространстве глобальных переменных; при первом обращении к функции инициализируется нулевым значением, и после выхода из функции из памяти не удаляется (таким образом, при последующих обращениях к функции в ней содержится старое значение);

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

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

int plus5()

{

static int x;

return x + 5;

}

 

void main()

{

int y;

у = plus5();      //y = 5

у = plus5();      //y = 10

у = plus5();      //у = 15

}

 

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

int   f1 (int);

void   f2(int, int);

int   f1(int x)

void   f2(int a, int b)

{

}

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

 

Рекурсия

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

void f1(int n)

{

int x;

fl(x);

}

Несмотря на сложность восприятия, рекурсия довольно часто используется в стандартных библиотечных функциях, а также во многих алгоритмах сортиров­ки. Тем не менее, при программировании микроконтроллеров использование ре­курсии, как правило, чревато проблемами из-за ограниченного объема оператив­ной памяти. Дело в том, что при каждом вызове рекурсивной функции часть памя­ти расходуется на сохранение данных, помещаемых в стек. Эти данные хранятся там до тех пор, пока не будет выполнен возврат из функции. Таким образом, когда рекурсивная функция снова и снова вызывает саму себя, в стеке остается все меньше и меньше свободной памяти.

Можно сказать, что в подавляющем большинстве случаев использование ре­курсии при программировании микроконтроллеров — это надежный способ быст­рого и непредсказуемого заполнения стека. Это очень часто приводит к возникно­вению ошибочного состояния, называемого переполнением стека. Область стека обычно размещается в верхней части памяти и растет "вниз", тогда, как область переменных размещается в нижней части памяти и растет "вверх". Поэтому если объем данных, помещаемых в стек, превысит размер области стека, эти данные могут достигнуть области переменных и затереть собой ее значения. При этом ошибку переполнения стека не всегда легко выявить, поскольку она может прояв­ляться лишь периодически.

 

ПРИМЕЧАНИЕ

Подобная проблема может также возникнуть при многократном вложении функций, то есть когда функция f1 вызывает функцию f2, которая вызывает функцию f3. которая вызывает функцию f4 и т.д.

Hosted by uCoz