Обучение : Программирование на Си для PIC


Как работают функции. Комментарии.

У вас должен возникнуть закономерный вопрос – почему по-разному записаны функции, хотя я и говорю что это одно и то же: 
void main (void){}   и   main (){}   ?
Что такое void ? Для чего нужен void или не нужен совсем?

 

Вспомните что такое функция. Функция – это совокупность команд, обычно предназначенных для решения определенной задачи. А какой или каких задач? Любых (шучу). Любых задач связанных с обсчетом данных (чисел). Соответственно, мы можем функции передавать данные для обсчета, можем получать обсчитанные данные, можем и передавать и получать, а можем ничего не передавать и ничего не получать. Иначе говорят так – «передаем значение функции» или «функция возвращает значение».

 

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

 

Теперь рассмотрим некую функцию, которая называется «sum»:

 

unsigned int sum (unsigned int x, unsigned char y)

unsigned int перед sum – означает, что функция возвращает значение типа «целое без знака».

 

unsigned int x, unsigned char y – означает что в функцию мы передаем значение типа «целое без знака» которое равно х, а также передаем значение типа «символ без знака» которое равно y.

 

 

Важный вывод. Фраза до фразы sum показывает какой тип данных будет возвращен функцией. Необходимо понимать, функция возвратит только одно значение (одно число). Необходимо также понимать, что функция не может возвращать одновременно больше одного значения. Либо одно значение одного из типов, либо ничего (пустое), т.е. void.

 

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

 

Как бы мы не назвали функцию (хоть zadnica) мы обязательно должны сказать, что передаем в функцию и что по результатам вычислений функция возвращает, ну например, void zadnica (long int her).

 

Чтобы понять, как это работает, напишем маленький текст программы.

 

 

// объявляем функции

unsigned int sum (unsigned int x, unsigned char y); // суммирование

void del (void); // деление

 

// объявляем глобальные переменные

unsigned int z;

 

// описание функции суммирования

unsigned int sum (unsigned int x, unsigned char y)

{

return x+y;

}

 

// начало программы

void main (void){

unsigned int q; // локальная переменная q

q = sum (5,1); /* вызываем функцию sum и передаем ей значения 5 и 1,

результат вызова sum (5,1) передаем q */

z = q; // значение из q передаем z

del();} // вызываем функцию деления

 

// описание функции деления

void del (void)

{

z = z/2; // результат деления z/2 передаем снова в z

}

 

 

Заголовочный файл и строчку конфигурации я не стал писать в этом примере. Вы уже должны понимать о чем я говорю. Ну а теперь давайте разбирать. На всё это мы будем смотреть глазами компилятора (т.е. глазами переводчика программы с языка Си в машинные коды).

 

В тексте программы мы видим комментария. Комментарии начинаются с символов // и распространяются в пределах одной строчки. Также комментария могут быть многострочными. Текст, который стоит между /* */ будет закомментирован. Эти символы (/**/) удобно набирать в дополнительной цифровой секции клавиатуры (рядом с кнопкой NumLock). Компилятор игнорирует комментария и пустые строчки.

 

Для того, чтобы функция могла быть использована в программе – её нужно объявить и описАть. Объявление – это заголовок функции, например, void primer (void); . А описание, это – это заголовок с телом функции (под телом понимаются команды внутри фигурных скобок).

 

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

 

Объявление и описание функции не является обязательным правилом для функции main , т.к. мы знаем, что функция main обязательно должна быть в тексте. И по логике работы микроконтроллера ни кто не может вызвать функцию main и в функцию main ничего не передается. Отсюда и послабления в оформлении текста программы: функция main может не описываться; также до и после её названия могут не указываются типы данных. Таким образом, void main (void) соответствует main () (круглые скобки после main() строго нужны).

 

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

 

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

 

Рассмотрим функцию main

// начало программы

void main (void){

unsigned int q; // локальная переменная q

q = sum (5,1); /* вызываем функцию sum и передаем ей значения 5 и 1,

результат вызова sum (5,1) передаем q */

z = q; // значение из q передаем z

del();} // вызываем функцию деления

 

Первая строчка после фигурной скобки  unsigned int q; – некая локальная переменная «кю». Нам эта переменная требуется для временного хранения результатов расчетов внутри функции. После выполнения функции эта переменная «разрушается». Физически под эту переменную компилятор выделит ячейки памяти в оперативной (регистровой) памяти. Локальная переменная или несколько локальных переменных ставятся в самом начале тела функции.

 

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

 

void main(void){unsigned int q;q=sum(5,6);z=q;del();}

 

Что такое q = sum (5,1);?  В этом месте программы будет вызвана функция sum, которой будет передано два значения 5 и 1. Необходимо понимать, что объявление вызываемой функции должно по тексту программы находиться раньше, чем будет сделан вызов функции. Объявления функций, это лишь «уведомления» для компилятора, что рано или поздно или никогда будет вызвана функция. Итак, вызов состоялся. Дальше компилятор перемещается в текст функции sum.

 

unsigned int sum (unsigned int x, unsigned char y)

{

return x+y;

}

 

А в функции sum всего лишь одна строчка: сложить x и y и вернуть результат (return). Функция sum не знает, кто заберет результат вычислений. Кто вызовет, тот и получит. А вызывает у нас строчка

 

q = sum (5,1);

 

В итоге в q у нас будет сумма двух чисел 5 и 1. Обратите внимание какие типы значений у нас заявлены в функции. Складывается беззнаковый инт и беззнаковый чар. Результат вычисления передается в беззнаковый инт. Т.е. величина складываемых чисел допустима к выбранному типу данных. Однако, для экономии программного кода, следует правильно продумывать размерность обсчитываемых чисел и выбор соответствующего типа данных. В нашем примере эта нерациональность сделана для наглядности.

 

Итак, в q у нас сидит число 6.

 

Следующая строчка тупо передает значение из q в z . Теперь у нас в z сидит число 6. Можно было бы сразу написать z = sum (5,1); но мы не ищем легких путей. Физически под эту переменную компилятор выделит ячейки памяти в оперативной (регистровой) памяти, и эти ячейки будут зарезервированы всё время за z, пока на кристалл идет питание.

 

В тексте программы мы объявили так называемые глобальные переменные. Что это значит? А вы чувствуете интуитивно разницу между локальными и глобальными переменными? Глобальные переменные могут использоваться всеми функциями. А вот локальные переменные, только внутри соответствующей функции и потом они «разрушаются». Освободившаяся оперативная память используется другими локальными переменными. Для чего я это говорю. Экономнее ребята, экономнее. Оперативной памяти не так много. Поэтому разумно используйте глобальные переменные.

 

Имена любых переменных обязательно начинаются с символов латинских букв, а не с цифр; буквы верхнего и нижнего регистров это разные буквы; имена переменных не могут совпадать с именами функций.  У глобальных переменных всегда уникальные имена, которые не совпадают с именами локальных переменных. Локальные переменные в разных функциях могут иметь одинаковые имена, чего я и придерживаюсь – во всех локальных переменных задаю имя tmp (перечитайте этот абзац) (перечитайте дважды).

 

 

del();} // вызываем функцию деления

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

 

void del (void)

{

z = z/2; // результат деления z/2 передаем снова в z

}

 

Т.к. ранее мы в z поместили число 6, то в результате вызова этой функции будет произведено деление 6/2 и получено число 3, которое затем будет записано назад в глобальную переменную z.

 

Важные выводы.

1) Функция может вызывать другую функцию.

2) В функцию для обсчета могут передаваться несколько значений.

3) Результатом вызова функции может стать возврат строго одного значения для некой переменной.

4) Функция обсчитывает глобальные и локальные переменные.

5) Прежде чем вызвать функцию, её нужно объявить и описать.

6) Описания функций могут находиться в любом месте программы и чередоваться как угодно

7) Объявление функции должно находиться перед вызовом функции.

8) Глобальные переменные объявляются перед их использованием.

9) Глобальные переменные имеют уникальные имена.

10) Локальные переменные объявляются в теле функции в самом начале функции.

11) Имена локальных переменных в разных функциях могут совпадать.

12) Тело функции – это текст в фигурных скобках.

13) Комментарии пишутся после // , либо между /* */.

14) Функции могут быть написаны в одну строчку.

15) Функции имеют уникальные имена.

16) Имена не должны начинаться с цифр.

17) В дерево проекта можно подключать иные файлы-исходники, но только один из файлов должен содержать функцию main, т.е. компилятор ищет не файл с именем main.c , а функцию с таким именем в подключенных файлах.


<<< назад далее >>>

Просмотров: 70840

 








 
 
 

В русском Интернете бестолку защищать свои права. Хотите использовать материалы - используйте,
но с письменного согласия авторов. В противном случае будут высланы соответствующие письма
в поисковые системы об ограничении индексации ваших сайтов. Не доводите до греха.