C как сделать описание функции
Перейти к содержимому

C как сделать описание функции

  • автор:

Функции в С++

До сих пор мы писали программы единым, функционально неделимым, кодом. Алгоритм программы находился в главной функции, причём других функций в программе не было. Мы писали маленькие программы, поэтому не было потребности в объявлении своих функций. Для написания больших программ, опыт показывает, что лучше пользоваться функциями. Программа будет состоять из отдельных фрагментов кода, под отдельным фрагментом кода понимается функция. Отдельным, потому, что работа отдельной функции не зависит от работы какой-нибудь другой. То есть алгоритм в каждой функции функционально достаточен и не зависим от других алгоритмов программы. Однажды написав функцию, её можно будет с лёгкостью переносить в другие программы. Функция (в программировании) — это фрагмент кода или алгоритм, реализованный на каком-то языке программирования, с целью выполнения определённой последовательности операций. Итак, функции позволяют сделать программу модульной, то есть разделить программу на несколько маленьких подпрограмм (функций), которые в совокупности выполняют поставленную задачу. Еще один огромнейший плюс функций в том, что их можно многократно использовать. Данная возможность позволяет многократно использовать один раз написанный код, что в свою очередь, намного сокращает объем кода программы!

Кроме того, что в С++ предусмотрено объявление своих функций, также можно воспользоваться функциями определёнными в стандартных заголовочных файлах языка программирования С++. Чтобы воспользоваться функцией, определённой в заголовочном файле, нужно его подключить. Например, чтобы воспользоваться функцией, которая возводит некоторое число в степень, нужно подключить заголовочный файл и в запустить функцию pow() в теле программы. Разработаем программу, в которой запустим функцию pow() .

// inc_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" //действие 1 - подключаем заголовочный файл который содержит прототипы основных математических функций #include int main(int argc, char* argv[]) < float power = pow(3.14,2); //действие 2 - запуск функции возведения числа в степень return 0; >
// inc_func.cpp: определяет точку входа для консольного приложения. //действие 1 - подключаем заголовочный файл который содержит прототипы основных математических функций #include int main(int argc, char* argv[]) < float power = pow(3.14,2); //действие 2 - запуск функции возведения числа в степень return 0; >

Подключение заголовочных файлов выполняется так, как показано в строке 5, т. е. объявляется препроцессорная директива #include , после чего внутри знаков <> пишется имя заголовочного файла. Когда подключен заголовочный файл, можно использовать функцию, что, и сделано в строке 9. Функция pow() возводит число 3.14 в квадрат и присваивает полученный результат переменной power , где
pow — имя функции;
числа 3.14 и 2 — аргументы функции;

Всегда после имени функции ставятся круглые скобочки, внутри которых, функции передаются аргументы, и если аргументов несколько, то они отделяются друг от друга запятыми. Аргументы нужны для того, чтобы функции передать информацию. Например, чтобы возвести число 3.14 в квадрат используя функцию pow() , нужно как-то этой функции сообщить, какое число, и в какую степень его возводить. Вот именно для этого и придуманы аргументы функций, но бывают функции, в которых аргументы не передаются, такие функции вызываются с пустыми круглыми скобочками. Итак, для того, чтобы воспользоваться функцией из стандартного заголовочного файла С++ необходимо выполнить два действия:

  1. Подключить необходимый заголовочный файл;
  2. Запустить нужную функцию.

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

  1. Функции, которые не возвращают значений
  2. Функции, возвращающие значение

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

// структура объявления функций не возвращающих значений void /*имя функции*/(/*параметры функции*/) // заголовок функции < // тело функции >

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

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; // объявление функции нахождения n! void faktorial(int numb)// заголовок функции < int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i int main(int argc, char* argv[]) < int digit; // переменная для хранения значения n! cout > digit; faktorial(digit);// запуск функции нахождения факториала system("pause"); return 0; >
// struct_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; // объявление функции нахождения n! void faktorial(int numb)// заголовок функции < int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i int main(int argc, char* argv[]) < int digit; // переменная для хранения значения n! cout > digit; faktorial(digit);// запуск функции нахождения факториала return 0; >

После того, как были подключены все необходимые заголовочные файлы, можно объявлять функцию нахождения факториала.Под объявлением функции подразумевается выбор имени функции, определение параметров функции и написание алгоритма, который является телом функции. После выполнения этих действий функцию можно использовать в программе. Так как функция не должна возвращать значение, то тип возвращаемых данных должен быть void . Имя функции — faktorial , внутри круглых скобочек объявлена переменная numb типа int . Эта переменная является параметром функции faktorial() . Таким образом, все объявления в строке 8 в совокупности составляют заголовок функции. Строки 9 — 14 составляют тело функции faktorial() . Внутри тела в строке 10 объявлена переменная rezult , которая будет хранить результат нахождения n! После чего, в строках 11-12 Объявлен оператор цикла for для нахождения факториала. В строке 13 объявлен оператор cout , с помощью которого значение факториала будет печататься на экране. Теперь, когда функция объявлена можно воспользоваться ею. В строке 21 запускается функция faktorial(digit) , внутри скобочек функции передаётся аргумент, т. е. значение, содержащееся в переменной digit . Результат работы программы (см. Рисунок 1).

CppStudio.com

Enter number: 5 5! = 120 Для продолжения нажмите любую клавишу . . .

Рисунок 1 — Функции в С++

Итак, после запуска программы, было введена цифра 5, и программа вычислила, что значение 120 это 5!.

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

// структура объявления функций возвращающих значения /*возвращаемый тип данных*/ /*имя функции*/(/*параметры функции*/) // заголовок функции < // тело функции return /*возвращаемое значение*/; >

Структура объявления функций осталась почти неизменной, за исключением двух строк. В заголовке функции сначала нужно определять возвращаемый тип данных, это может быть тип данных int , если необходимо возвратить целое число или тип данных float — для чисел с плавающей точкой. В общем, любой другой тип данных, всё зависит от того, что функция должна вернуть. Так как функция должна вернуть значение, то для этого должен быть предусмотрен специальный механизм, как в строке 5. C помощью зарезервированного слова return можно вернуть значение, по завершении работы функции. Всё, что нужно, так это указать переменную, содержащую нужное значение, или некоторое значение, после оператора return . Тип данных возвращаемого значения в строке 5 должен совпадать с типом данных в строке 2. Переделаем программу нахождения факториала так, чтобы функция faktorial() возвращала значение факториала.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; // объявление функции нахождения n! int faktorial(int numb)// заголовок функции < int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i int main(int argc, char* argv[]) < int digit; // переменная для хранения значения n! cout > digit; cout 
// struct_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; // объявление функции нахождения n! int faktorial(int numb)// заголовок функции < int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i int main(int argc, char* argv[]) < int digit; // переменная для хранения значения n! cout > digit; cout 

Теперь функция faktorial() имеет возвращаемый тип данных — int , так как n! — это целое число. В строке 13 объявлен оператор return , который возвращает значение, содержащееся в переменной rezult . В строке 21 выполняем запуск функции faktorial() , возвращаемое значение которой отправляем в поток вывода с помощью оператора cout . Можно было бы написать так int fakt = faktorial(digit); — переменной типа int присваиваем возвращаемое значение функции faktorial() , после чего в переменной fakt будет храниться значение n! . Результат работы программы не изменился (см. Рисунок 2).

CppStudio.com

Enter number: 5 5! = 120 Для продолжения нажмите любую клавишу . . .

Рисунок 2 — Функции в С++

Мы рассмотрели два типа функций, причём объявление функций выполняли в области программы, после подключения заголовочных файлов, но до начала функции main() . Существует несколько способов объявления функций (см. Рисунок 3).

Функции в С++

Рисунок 3 — Функции в С++

На рисунке 3 показаны 4 способа объявления функций в языке программирования С++. Рассмотрим структуры объявления функций в одном файле, с главной функцией. Функции можно объявлять в двух областях, до начала функции main() , после функции main() . До сих пор мы объявляли функции в одном файле, перед функцией main() — это самый простой из способов.

// struct_func.cpp: определяет точку входа для консольного приложения. #include «stdafx.h» /*область 1 — объявление функций до начала main() место для объявления функций функциям объявленным в этой области не нужны прототипы */ int main(int argc, char* argv[])

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

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" // место для объявления прототипов функций int main(int argc, char* argv[]) < return 0; >/*область 2 - объявление функций после main() место для объявления функций */
// struct_func.cpp: определяет точку входа для консольного приложения. // место для объявления прототипов функций int main(int argc, char* argv[]) < return 0; >/*область 2 - объявление функций после main() место для объявления функций */

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

// синтаксис объявления прототипа /*тип возвращаемых данных функции*/ /*имя функции*/(/*параметры функции*/);

Структура объявления прототипа очень схожа со структурой объявления функции. Разработаем программу, которая определяет, является ли введённое пятизначное число палиндромом. Палиндром — это число или текст, который читается одинаково как слева, так и справа: 93939; 49094; 11311.

// palindrom_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел int main(int argc, char* argv[]) < cout > in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел < int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый разряд пятизначного числа number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй разряд number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый разряд number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый разряд if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение >
// palindrom_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел int main(int argc, char* argv[]) < cout > in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел < int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый разряд пятизначного числа number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй разряд number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый разряд number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый разряд if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение >

В строке 7 объявлен прототип функции нахождения палиндрома пятизначных чисел. Обратите внимание на то, что прототип полностью должен совпадать с заголовком функции, но некоторые отличия все же есть. Например, то, что в прототипе имена параметров перечислять не надо, достаточно объявить их типы данных. В конце объявления прототипа всегда нужно ставить точку с запятой. В остальном объявление прототипа совпадает с объявлением заголовка одной функции. Прототип необходимо объявлять для каждой функции отдельно. Переменная out_number служит для временного хранения введённого числа. В строке 16 в условии оператора выбора if выполняется запуск функции palindrom5() . Аргументом для неё является переменная in_number . функция вернёт значение типа bool , и если функция вернёт true , то условие будет истинно, в противном случае — ложно. Можно было бы сначала присвоить значение, возвращаемое функцией, некоторой переменной, а потом эту переменную подставить в условие оператора выбора if , но это бы увеличило код программы на одну строку. В строках 23 — 40 объявлена функция palindrom5() , с одним параметром, через который функции передаётся пятизначное число. Переменные balance1 , balance2 , balance4 , balance5 объявлены в строке 25, и необходимы для хранения разрядов пятизначного числа: первого, второго, четвёртого, пятого (нумерация — справа на лево). В строках 26, 29, 32, 35 выполняется одна и та же операция — остаток от деления. Операция остаток от деления отсекает по одному разряду справа налево и сохраняет их в переменные balance1 , balance2 , balance4 , balance5 соответственно. Причём операция остаток от деления чередуется с операцией обычного деления. Операция деления выполняется в строках 27, 30, 33, и уменьшает введённое пятизначное число за шаг на один разряд. В строке 30 операция деления уменьшает введённое число сразу на два разряда, потому, что число пятизначное и средний разряд нас не интересует, он может быть любым. В строках 36 — 39 объявлен оператор выбора if , который сравнивает разряды пятизначного числа, и если они, соответствующим образом, равны, то функция вернёт значение true , иначе — false . Результат работы программы (см. Рисунок 4).

CppStudio.com

Enter 5zn-e chislo: 12321 Number 12321 - palendrom Для продолжения нажмите любую клавишу . . .

Рисунок 4 — Функции в С++

До сих пор мы объявляли функции в одном файле, с основной программой, то есть там, где находится функция main() . В С++ существует возможность поместить объявления функций в отдельный файл, тогда необходимо будет подключать файл с функциями, как в случае с подключением стандартных заголовочных файлов. Есть два способа:

  1. создание файла типа *.cpp, в котором объявляются функции;
  2. создание файлов типа *.cpp и *.h.

К хорошему стилю программирования относится второй способ. Таким образом, если объявлять функции в другом файле, то делать это согласно способу два. Переделаем программу нахождения палиндрома так, чтобы объявление функции palindrom5() находилось в отдельном файле *.cpp . Файл *.h нужен для того, чтобы скрыть реализацию функций, т. е. в файле *.h будут содержаться прототипы функций. С помощью обозревателя решений MVS создаём файл типа *.h и называем его palendrom .

// код файла palendrom.h #ifndef palendrom #define palendrom bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел #endif

Директивы в строках 2,3,5 необходимо всегда объявлять в файлах с прототипами функций, причём прототипы функций всегда объявляются в файлах типа *.h . После директив записанных в строках 2,3, но до директивы #endif объявляются прототипы функций. В строке 4 объявлен прототип функции palindrom5() . Объявление данной функции находится в файле palendrom.cpp , который предварительно тоже был создан с помощью обозревателя решений MVS.

// содержимое файла palendrom.cpp #include "stdafx.h" #include "palendrom.h" bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел < int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый остаток number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй остаток number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый остаток number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый остаток if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение >

Функции

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

Формальное определение функции выглядит следующим образом:

тип имя_функции(параметры)

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

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

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

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

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

Например, определение функции main, которая должна быть в любой программе на языке C++ и с которой начинается ее выполнение:

int main()

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

Стоит отметить, что С++ позволяет не использовать оператор return в функции main:

int main()

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

void hello()

Выполнение функции

Когда запускается программа на языке C++, то запускается функция main. Никакие другие функции, определенные в программе, автоматически не выполняются. Для выполнения функции ее необходимо вызвать. Вызов функции осуществляется в форме:

имя_функции(аргументы);

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

Например, определим и выполним простейшую функцию:

#include void hello() < std::cout int main()

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

hello hello

Объявление функции

При использовании функций стоит учитывать, что компилятор должен знать о функции до ее вызова. Поэтому вызов функции должен происходить после ее определения, как в случае выше. В некоторых языках это не имеет значение, но в языке C++ это играет большую роль. И если, к примеру, мы сначала вызовем, а потом определим функцию, то мы получим ошибку на этапе компиляции, как в следующем случае:

#include int main() < hello(); hello(); >void hello()

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

тип имя_функции(параметры);

Фактически это заголовок функции. То есть для функции hello объявление будет выглядеть следующим образом:

void hello();

Используем объявление функции:

#include void hello(); int main() < hello(); hello(); >void hello()

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

Функции в языке Си

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

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

Семантика функции определяет способ реализации функции. Обычно представляет собой тело функции.

Определение функции

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

  • тип возвращаемого значения;
  • имя функции;
  • информация о формальных аргументах;
  • тело функции.

Определение функции имеет следующий синтаксис:

ТипВозвращаемогоЗначения ИмяФункции(СписокФормальныхАргументов)
ТелоФункции;
.
return (ВозвращаемоеЗначение);
>

Пример : Функция сложения двух вещественных чисел

float function( float x, float z)
float y;
y=x+z;
return (y);
>

В указанном примере возвращаемое значение имеет тип float . В качестве возвращаемого значения в вызывающую функцию передается значение переменной y . Формальными аргументами являются значения переменных x и z .

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

Различают системные (в составе систем программирования) и собственные функции.

Системные функции хранятся в стандартных библиотеках, и пользователю не нужно вдаваться в подробности их реализации. Достаточно знать лишь их сигнатуру. Примером системных функций, используемых ранее, являются функции printf() и scanf() .

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

Разбиение программ на функции дает следующие преимущества:

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

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

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

Общий вид вызова функции

Переменная = ИмяФункции(СписокФактическихАргументов);

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

Возврат в вызывающую функцию

По окончании выполнения вызываемой функции осуществляется возврат значения в точку ее вызова. Это значение присваивается переменной, тип которой должен соответствовать типу возвращаемого значения функции. Функция может передать в вызывающую программу только одно значение. Для передачи возвращаемого значения в вызывающую функцию используется оператор return в одной из форм:

return (ВозвращаемоеЗначение);
return ВозвращаемоеЗначение;

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

Оператор return также завершает выполнение функции и передает управление следующему оператору в вызывающей функции. Оператор return не обязательно должен находиться в конце тела функции.

Функции могут и не возвращать значения, а просто выполнять некоторые вычисления. В этом случае указывается пустой тип возвращаемого значения void , а оператор return может либо отсутствовать, либо не возвращать никакого значения:

Пример : Посчитать сумму двух чисел.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include
// Функция вычисления суммы двух чисел
int sum( int x, int y) // в функцию передаются два целых числа
int k = x + y; // вычисляем сумму чисел и сохраняем в k
return k; // возвращаем значение k
>
int main()
int a, r; // описание двух целых переменных
printf( «a= » );
scanf( «%d» , &a); // вводим a
r = sum(a, 5); // вызов функции: x=a, y=5
printf( «%d + 5 = %d» , a, r); // вывод: a + 5 = r
getchar(); getchar(); // мы использовали scanf(),
return 0; // поэтому getchar() вызываем дважжы
>

Сумма двух чисел

Результат выполнения

В языке Си нельзя определять одну функцию внутри другой.

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

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

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

Если в примере выше тело функции сложения чисел разместить после тела функции main, то код будет выглядеть следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include
int sum( int , int ); // сигнатура
int main()
int a, r;
printf( «a= » );
scanf( «%d» , &a);
r = sum(a, 5); // вызов функции: x=a, y=5
printf( «%d + 5 = %d» , a, r);
getchar(); getchar();
return 0;
>
int sum( int x, int y) // семантика
int k;
k = x + y;
return (k);
>

Рекурсивные функции

Функция, которая вызывает сама себя, называется рекурсивной функцией .

Рекурсия — вызов функции из самой функции.

Пример рекурсивной функции — функция вычисления факториала.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include
int fact( int num) // вычисление факториала числа num
if (num else return num*fact(num — 1); // рекурсивный вызов для числа на 1 меньше
>
// Главная функция
int main()
int a, r;
printf( «a= » );
scanf( «%d» , &a);
r = fact(a); // вызов функции: num=a
printf( «%d! = %d» , a, r);
getchar(); getchar();
return 0;
>

Результат рекурсивного вычисления факториала

Результат выполнения

Более подробно рекурсивные функции рассмотрены в этой статье.

Математические функции

Математические функции хранятся в стандартной библиотеке math.h . Аргументы большинства математических функций имеют тип double . Возвращаемое значение также имеет тип double .
Углы в тригонометрических функциях задаются в радианах.

Основные математические функции стандартной библиотеки.

Функция Описание
int abs( int x) Модуль целого числа x
double acos( double x) Арккосинус x
double asin( double x) Арксинус x
double atan( double x) Арктангенс x
double cos( double x) Косинус x
double cosh( double x) Косинус гиперболический x
double exp( double x) Экспонента x
double fabs( double x) Модуль вещественного числа
double fmod( double x, double y) Остаток от деления x/y
double log( double x) Натуральный логарифм x
double log10( double x) Десятичный логарифм x
double pow( double x, double y) x в степени y
double sin( double x) Синус x
double sinh( double x) Синус гиперболический x
double sqrt( double x) Квадратный корень x
double tan( double x) Тангенс x
double tanh( double x) Тангенс гиперболический x

Особенности использования функций в языке C++ рассмотрены в этой статье.

Комментариев к записи: 107

Помогите решить мою проблему, я новичок в программировании и не могу понять почему у меня подчеркивает FILE* st

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include

struct FIO
char f[20];
char i[20];
char o[20];
int ng;
char bg[1];
int s;
>;
typedef struct FIO fio;

int main()
system( «chcp 1251 > null» );
int count = -1, probels = 0, n = 0; //создали переменную для подсчета строк
int * num = calloc(0, sizeof ( int )); //массив, записывающий номера правильных строк
FILE* st = fopen( «st.txt», «r» ); //открыли файл на чтение
while (!feof(st)) //проверяеем строки
char * str = calloc(100, sizeof ( char ));
fgets(str, 100, st); //считываем из файла
count++; //счетчик строк
//Проверяем символы строки
for ( int i = 0; i < strlen(str) - 1; i++) //strlen(str) cчитает вместе с \n ПОЭТОМУ ВАЖНО -1
if (str[i] == ‘ ‘)
probels++; //подсчет пробелов в строке (должно быть 5)
>
>
if (probels == 5) //записываем номер строки в массив
n++; //счетчик элементов массива
num = realloc(num, n * sizeof ( int ));
num[n — 1] = count; // n-1 = 0
>
free(str); //очищаем буфер
probels = 0;
>
fclose(st);
fio* array = calloc(n, sizeof (fio)); //выделяем память под структуру правильных строк (n — колич правильных строк)
int s = 0; // для массива num
st = fopen( «st.txt», «r» );
for ( int t = 0, s = 0; t if (t == num[s]) //номер строки = номеру правильной строки
fscanf(st, «%s %s %s %d %s %d\n» , array[s].f, array[s].i, array[s].o, &array[s].ng, array[s].bg, &array[s].s);
s++;
>
else
char * str = calloc(100, sizeof ( char ));
fgets(str, 100, st); //считываем из файла
>
>
fclose(st);
for ( int i = 0; i < n; i++)
printf( «%s %s %s %d %s %d\n» , array[i].f, array[i].i, array[i].o, array[i].ng, array[i].bg, array[i].s);
>
return 0;
>

#include
#include
#include

Добрый вечер! При изучении языка Си в колледже преподаватель задал такую задачку: по введенным в текстовый файл трем координатам, которые нужно считать из файла в консоль, необходимо определить существует ли данный треугольник, определить длину сторон, периметр и площадь, данную информацию нужно вывести в консоль и записать в другой текстовый файл. Задачу я решила, но одно из требований — нужно обязательно использовать модальность, то бишь выделить функции. Это у меня не получается от слова совсем((. Обидно, что задача решена, но я не могу вынести ничего в функции, не знаю какие значения в них передавать, мне кажется, что данный код вообще неделим и прекрасен таким какой он есть. Но преподаватель другого мнения(

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#define PATH «1.txt»

struct coordinata
float x1;
float y1;
float x2;
float y2;
float x3;
float y3;

>;
typedef struct coordinata treug;

int main( void )
system( «chcp 1251>nul» );
treug* buffer = malloc(1);
FILE* mf = fopen(PATH, «r» );
char * str = calloc(100, 4);
int count = 0;
for (size_t i = 0; !feof(mf); i++) //читаем строки из файла и записываем в экземпляр структуры
buffer = realloc(buffer, sizeof (treug) * (i + 1));
fgets(str, 100, mf);
sscanf(str, «%f%f%f%f%f%f» , &buffer[i].x1, &buffer[i].y1, &buffer[i].x2, &buffer[i].y2, &buffer[i].x3, &buffer[i].y3);
count++;
>
int n = count;
free(str);
fclose(mf);
FILE* myfile = fopen( «2.txt», «w» );
int status = 0;
float a, b, c,P,S,p=0;
float d1, d2, d3, d4, d5, d6;
for (size_t i = 0; i < n; i++) //выводим экземпляр структуры
printf( «\nКоординаты %d треугольника:\n» , i + 1);
printf( «%f %f %f %f %f %f\n» , buffer[i].x1, buffer[i].y1, buffer[i].x2, buffer[i].y2, buffer[i].x3, buffer[i].y3);

a = sqrt(pow((buffer[i].x2 — buffer[i].x1),2) + pow((buffer[i].y2 — buffer[i].y1), 2));
b = sqrt(pow((buffer[i].x3 — buffer[i].x2),2) + pow((buffer[i].y3 — buffer[i].y2), 2));
c = sqrt(pow((buffer[i].x1 — buffer[i].x3),2) + pow((buffer[i].y1 — buffer[i].y3), 2));
P = a + b + c;
p = P / 2;
S = sqrt(p * (p — a) * (p — b) * (p — c));
if (a + b > c && a + c > b && c + b > a && a > 0 && b > 0 && c > 0)
printf( «Данный треугольник существует\n» );
printf( «Сторона а = %f, сторона b = %f, сторона c = %f,\nПериметр треугольника равен %f\nПлощадь треугольника равна %f\n» ,a,b,c, P,S);
fprintf(myfile, «Сторона а = %f, сторона b = %f, сторона c = %f,\nПериметр треугольника равен %f\nПлощадь треугольника равна %f\n» , a, b, c, P, S);
fprintf(myfile, » % f % f % f % f % f % f\n» , buffer[i].x1, buffer[i].y1, buffer[i].x2, buffer[i].y2, buffer[i].x3, buffer[i].y3);
>
else printf( «Данный треугольник не существует» );

Функции

Теги: Функции в си, прототип, описание, определение, вызов. Формальные параметры и фактические параметры. Аргументы функции, передача по значению, передача по указателю. Возврат значения.

Введение

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

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

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

Мы уже знакомы с многими функциями и знаем, как их вызывать – это функции библиотек stdio, stdlib, string, conio и пр. Более того, main – это тоже функция. Она отличается от остальных только тем, что является точкой входа при запуске приложения.
Функция в си определяется в глобальном контексте. Синтаксис функции:

Самый простой пример – функция, которая принимает число типа float и возвращает квадрат этого числа

#include #include float sqr(float x) < float tmp = x*x; return tmp; >void main()

Внутри функции sqr мы создали локальную переменную, которой присвоили значение аргумента. В качестве аргумента функции передали число 9,3. Служебное слово return возвращает значение переменной tmp. Можно переписать функцию следующим образом:

float sqr(float x)

В данном случае сначала будет выполнено умножение, а после этого возврат значения. В том случае, если функция ничего не возвращает, типом возвращаемого значения будет void. Например, функция, которая печатает квадрат числа:

void printSqr(float x)

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

void printSqr(float x)

Если функция не принимает аргументов, то скобки оставляют пустыми. Можно также написать слово void:

void printHelloWorld()
void printHelloWorld(void)

Формальные и фактические параметры

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

Например, пусть есть функция, которая возвращает квадрат числа и функция, которая суммирует два числа.

#include #include //Формальные параметры имеют имена a и b //по ним мы обращаемся к переданным аргументам внутри функции int sum(int a, int b) < return a+b; >float square(float x) < return x*x; >void main() < //Фактические параметры могут иметь любое имя, в том числе и не иметь имени int one = 1; float two = 2.0; //Передаём переменные, вторая переменная приводится к нужному типу printf("%d\n", sum(one, two)); //Передаём числовые константы printf("%d\n", sum(10, 20)); //Передаём числовые константы неверного типа, они автоматически приводится к нужному printf("%d\n", sum(10, 20.f)); //Переменная целого типа приводится к типу с плавающей точкой printf("%.3f\n", square(one)); //В качестве аргумента может выступать и вызов функции, которая возвращает нужное значение printf("%.3f\n", square(sum(2 + 4, 3))); getch(); >

Обращаю внимание, что приведение типов просиходит неявно и только тогда, когда это возможно. Если функция получает число в качестве аргумента, то нельзя ей передать переменную строку, например «20» и т.д. Вообще, лучше всегда использовать верный тип или явно приводить тип к нужному.
Если функция возвращает значение, то оно не обязательно должно быть сохранено. Например, мы пользуемся функцией getch, которая считывает символ и возвращает его.

#include #include void main() < char c; do < //Сохраняем возвращённое значение в переменную c = getch(); printf("%c", c); >while(c != 'q'); //Возвращённое значение не сохраняется getch(); >

Передача аргументов

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

#include #include void change(int a) < a = 100; printf("%d\n", a); >void main()

Программы выведет
200
100
200
Понятно почему. Внутри функции мы работаем с переменной x, которая является копией переменной d. Мы изменяем локальную копию, но сама переменная d при этом не меняется. После выхода из функции локальная переменная будет уничтожена. Переменная d при этом никак не изменится.
Каким образом тогда можно изменить переменную? Для этого нужно передать адрес этой переменной. Перепишем функцию, чтобы она принимала указатель типа int

#include #include void change(int *a) < *a = 100; printf("%d\n", *a); >void main()

Вот теперь программа выводит
200
100
100
Здесь также была создана локальная переменная, но так как передан был адрес, то мы изменили значение переменной d, используя её адрес в оперативной памяти.

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

#include #include #include void init(int *a, unsigned size) < a = (int*) malloc(size * sizeof(int)); >void main() < int *a = NULL; init(a, 100); if (a == NULL) < printf("ERROR"); >else < printf("OKAY. "); free(a); >getch(); >

Но эта функция выведет ERROR. Мы передали адрес переменной. Внутри функции init была создана локальная переменная a, которая хранит адрес массива. После выхода из функции эта локальная переменная была уничтожена. Кроме того, что мы не смогли добиться нужного результата, у нас обнаружилась утечка памяти: была выделена память на куче, но уже не существует переменной, которая бы хранила адрес этого участка.

Для изменения объекта необходимо передавать указатель на него, в данном случае – указатель на указатель.

#include #include #include void init(int **a, unsigned size) < *a = (int*) malloc(size * sizeof(int)); >void main() < int *a = NULL; init(&a, 100); if (a == NULL) < printf("ERROR"); >else < printf("OKAY. "); free(a); >getch(); >

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

#include #include #include #include char* initByString(const char *str) < char *p = (char*) malloc(strlen(str) + 1); strcpy(p, str); return p; >void main()

В этом примере утечки памяти не происходит. Мы выделили память с помощью функции malloc, скопировали туда строку, а после этого вернули указатель. Локальные переменные были удалены, но переменная test хранит адрес участка памяти на куче, поэтому можно его удалить с помощью функции free.

Объявление функции и определение функции. Создание собственной библиотеки

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

#include #include //Прототипы функций. Имена аргументов можно не писать int odd(int); int even(int); void main() < printf("if %d odd? %d\n", 11, odd(11)); printf("if %d odd? %d\n", 10, odd(10)); getch(); >//Определение функций int even(int a) < if (a) < odd(--a); >else < return 1; >> int odd(int a) < if (a) < even(--a); >else < return 0; >>

Это смешанная рекурсия – функция odd возвращает 1, если число нечётное и 0, если чётное.

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

Давайте создадим простую библиотеку. Для этого нужно будет создать два файла – один с расширением .h и поместить туда прототипы функций, а другой с расширением .c и поместить туда определения этих функций. Если вы работаете с IDE, то .h файл необходимо создавать в папке Заголовочные файлы, а файлы кода в папке Файлы исходного кода. Пусть файлы называются File1.h и File1.c
Перепишем предыдущий код. Вот так будет выглядеть заголовочный файл File1.h

#ifndef _FILE1_H_ #define _FILE1_H_ int odd(int); int even(int); #endif

Содержимое файла исходного кода File1.c

#include "File1.h" int even(int a) < if (a) < odd(--a); >else < return 1; >> int odd(int a) < if (a) < even(--a); >else < return 0; >>

Наша функция main

#include #include #include «File1.h» void main()

Рассмотрим особенности каждого файла. Наш файл, который содержит функцию main, подключает необходимые ему библиотеки а также заголовочный файл File1.h. Теперь компилятору известны прототипы функций, то есть он знает возвращаемый тип, количество и тип аргументов и имена функций.

Заголовочный файл, как и оговаривалось ранее, содержит прототип функций. Также здесь могут быть подключены используемые библиотеки. Макрозащита #define _FILE1_H_ и т.д. используется для предотвращения повторного копирования кода библиотеки при компиляции. Эти строчки можно заменить одной

#pragma once int odd(int); int even(int);

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

Передача массива в качестве аргумента

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

#include #include void printArray(int *arr, unsigned size) < unsigned i; for (i = 0; i < size; i++) < printf("%d ", arr[i]); >> void main() < int x[10] = ; printArray(x, 10); getch(); >

В этом примере функция может иметь следующий вид

void printArray(int arr[], unsigned size) < unsigned i; for (i = 0; i < size; i++) < printf("%d ", arr[i]); >>

Также напомню, что правило подмены массива на указатель не рекурсивное. Это значит, что необходимо указывать размерность двумерного массива при передаче

#include #include void printArray(int arr[][5], unsigned size) < unsigned i, j; for (i = 0; i < size; i++) < for (j = 0; j < 5; j++) < printf("%d ", arr[i][j]); >printf("\n"); > > void main() < int x[][5] = < < 1, 2, 3, 4, 5>, < 6, 7, 8, 9, 10>>; printArray(x, 2); getch(); >

Либо, можно писать

#include #include void printArray(int (*arr)[5], unsigned size) < unsigned i, j; for (i = 0; i < size; i++) < for (j = 0; j < 5; j++) < printf("%d ", arr[i][j]); >printf("\n"); > > void main() < int x[][5] = < < 1, 2, 3, 4, 5>, < 6, 7, 8, 9, 10>>; printArray(x, 2); getch(); >

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

#include #include #include #include #define SIZE 10 unsigned* getLengths(const char **words, unsigned size) < unsigned *lengths = NULL; unsigned i; lengths = (unsigned*) malloc(size * sizeof(unsigned)); for (i = 0; i < size; i++) < lengths[i] = strlen(words[i]); >return lengths; > void main() < char **words = NULL; char buffer[128]; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) < printf("%d. ", i); scanf("%127s", buffer); words[i] = (char*) malloc(128); strcpy(words[i], buffer); >len = getLengths(words, SIZE); for (i = 0; i < SIZE; i++) < printf("%d ", len[i]); free(words[i]); >free(words); free(len); getch(); >

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

#include #include #include #include #define SIZE 10 void getLengths(const char **words, unsigned size, unsigned *out) < unsigned i; for (i = 0; i < size; i++) < out[i] = strlen(words[i]); >> void main() < char **words = NULL; char buffer[128]; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) < printf("%d. ", i); scanf("%127s", buffer); words[i] = (char*) malloc(128); strcpy(words[i], buffer); >len = (unsigned*) malloc(SIZE * sizeof(unsigned)); getLengths(words, SIZE, len); for (i = 0; i < SIZE; i++) < printf("%d ", len[i]); free(words[i]); >free(words); free(len); getch(); >

На этом первое знакомство с функциями заканчивается: тема очень большая и разбита на несколько статей.

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *