Функции в C++ — урок 6
Сегодня мы поговорим о функциях в C++. Очень часто в программировании необходимо выполнять одни и те же действия. Например, мы хотим выводить пользователю сообщения об ошибке в разных местах программы, если он ввел неверное значение. без функций это выглядело бы так:
#include #include using namespace std; int main() < string valid_pass = "qwerty123"; string user_pass; cout else < cout return 0; >
А вот аналогичный пример с функцией:
#include #include using namespace std; void check_pass (string password) < string valid_pass = "qwerty123"; if (password == valid_pass) < cout else < cout > int main()
По сути, после компиляции не будет никакой разницы для процессора, как для первого кода, так и для второго. Но ведь такую проверку пароля мы можем делать в нашей программе довольно много раз. И тогда получается копипаста и код становится нечитаемым. Функции — один из самых важных компонентов языка C++.
- Любая функция имеет тип, также, как и любая переменная.
- Функция может возвращать значение, тип которого в большинстве случаев аналогично типу самой функции.
- Если функция не возвращает никакого значения, то она должна иметь тип void (такие функции иногда называют процедурами)
- При объявлении функции, после ее типа должно находиться имя функции и две круглые скобки — открывающая и закрывающая, внутри которых могут находиться один или несколько аргументов функции, которых также может не быть вообще.
- после списка аргументов функции ставится открывающая фигурная скобка, после которой находится само тело функции.
- В конце тела функции обязательно ставится закрывающая фигурная скобка.
Пример построения функции
#include using namespace std; void function_name () < cout int main() < function_name(); // Вызов функции return 0; >
Перед вами тривиальная программа, Hello, world, только реализованная с использованием функций.
Если мы хотим вывести «Hello, world» где-то еще, нам просто нужно вызвать соответствующую функцию. В данном случае это делается так: function_name(); . Вызов функции имеет вид имени функции с последующими круглыми скобками. Эти скобки могут быть пустыми, если функция не имеет аргументов. Если же аргументы в самой функции есть, их необходимо указать в круглых скобках.
Также существует такое понятие, как параметры функции по умолчанию. Такие параметры можно не указывать при вызове функции, т.к. они примут значение по умолчанию, указанно после знака присваивания после данного параметра и списке всех параметров функции.
В предыдущих примерах мы использовали функции типа void , которые не возвращают никакого значения. Как многие уже догадались, оператор return используется для возвращения вычисляемого функцией значения.
Рассмотрим пример функции, возвращающей значение на примере проверки пароля.
#include #include using namespace std; string check_pass (string password) < string valid_pass = "qwerty123"; string error_message; if (password == valid_pass) < error_message = "Доступ разрешен."; >else < error_message = "Неверный пароль!"; >return error_message; > int main()
В данном случае функция check_pass имеет тип string, следовательно она будет возвращать только значение типа string, иными словами говоря строку. Давайте рассмотрим алгоритм работы этой программы.
Самой первой выполняется функция main(), которая должна присутствовать в каждой программе. Теперь мы объявляем переменную user_pass типа string, затем выводим пользователю сообщение «Введите пароль», который после ввода попадает в строку user_pass. А вот дальше начинает работать наша собственная функция check_pass() .
В качестве аргумента этой функции передается строка, введенная пользователем.
Аргумент функции — это, если сказать простым языком переменные или константы вызывающей функции, которые будет использовать вызываемая функция.
При объявлении функций создается формальный параметр, имя которого может отличаться от параметра, передаваемого при вызове этой функции. Но типы формальных параметров и передаваемых функии аргументов в большинстве случаев должны быть аналогичны.
После того, как произошел вызов функции check_pass() , начинает работать данная функция. Если функцию нигде не вызвать, то этот код будет проигнорирован программой. Итак, мы передали в качестве аргумента строку, которую ввел пользователь.
Теперь эта строка в полном распоряжении функции (хочу обратить Ваше внимание на то, что переменные и константы, объявленные в разных функциях независимы друг от друга, они даже могут иметь одинаковые имена. В следующих уроках я расскажу о том, что такое область видимости, локальные и глобальные переменные).
Теперь мы проверяем, правильный ли пароль ввел пользователь или нет. если пользователь ввел правильный пароль, присваиваем переменной error_message соответствующее значение. если нет, то сообщение об ошибке.
После этой проверки мы возвращаем переменную error_message . На этом работа нашей функции закончена. А теперь, в функции main(), то значение, которое возвратила наша функция мы присваиваем переменной error_msg и выводим это значение (строку) на экран терминала.
Также, можно организовать повторный ввод пароля с помощью рекурсии (о ней мы еще поговорим). Если объяснять вкратце, рекурсия — это когда функция вызывает сама себя. Смотрите еще один пример:
#include #include using namespace std; bool password_is_valid (string password) < string valid_pass = "qwerty123"; if (valid_pass == password) return true; else return false; >void get_pass () < string user_pass; cout else < cout > int main()
Функции очень сильно облегчают работу программисту и намного повышают читаемость и понятность кода, в том числе и для самого разработчика (не удивляйтесь этому, т. к. если вы откроете код, написанный вами полгода назад,не сразу поймете соль, поверьте на слово).
Не расстраивайтесь, если не сразу поймете все аспекты функций в C++, т. к. это довольно сложная тема и мы еще будем разбирать примеры с функциями в следующих уроках.
Совет: не бойтесь экспериментировать, это очень хорошая практика, а после прочтения данной статьи порешайте элементарные задачи, но с использованием функций. Это будет очень полезно для вас.
Если Вы найдете какие-либо ошибки в моем коде, обязательно напишите об этом в комментариях. здесь же можно задавать все вопросы.
Функции
Функция определяет действия, которые выполняет программа. Функции позволяют выделить набор инструкций и назначить ему имя. А затем многократно по присвоенному имени вызывать в различных частях программы. По сути функция — это именованный блок кода.
Формальное определение функции выглядит следующим образом:
тип имя_функции(параметры)
Первая строка представляет заголовок функции. Вначале указывается возвращаемый тип функции. Если функция не возвращает никакого значения, то используется тип 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, и никаких проблем в работе программы не возникнет.
Пользовательские функции в Си
Итак, зачем нужны пользовательские функции? Пользовательские функции нужны для того, чтобы программистам было проще писать программы.
Помните, мы говорили о парадигмах программирования, а точнее о структурном программировании. Основной идеей там было то, что любую программу можно написать используя только три основных конструкции: следование, условие и цикл. Теперь к этим конструкциям мы добавим ещё одну – «подпрограммы» – и получим новую парадигму процедурное программирование» .
Отличие лишь в том, что отдельные кусочки нашей основной программы (в частности, повторяющиеся) мы будем записывать в виде отдельных функций (подпрограмм, процедур) и по мере необходимости их вызывать. По сути, программа теперь будет описывать взаимодействие различных функций.
В принципе, мы уже используем эту парадигму. Если вам пока ещё не совсем ясно, почему это проще, то просто представьте, что вместо того чтобы вызвать функцию exp(x) из заголовочного файла math.h вам каждый раз необходимо было бы описывать подробно, как вычислить значение этой функции.
Итак, в этом уроке мы подробно обсудим то, как функции устроены изнутри. А также научимся создавать свои собственные пользовательские функции.
Как устроены функции
Вспомним информацию с первого урока. Все функции, в том числе и те, которые пишет пользователь, устроены сходным образом. У них имеется две основных составных части: заголовок функции и тело функции.
int main(void)< // заголовок функции // в фигурных скобках записано тело функции >
С телом функции всё ясно: там описывается алгоритм работы функции. Давайте разберёмся с заголовком. Он состоит из трёх обязательных частей:
- тип возвращаемого значения;
- имя функции;
- аргументы функции.
Сначала записывается тип возвращаемого значения, например, int , как в функции main . Если функция не должна возвращать никакое значение в программу, то на этом месте пишется ключевое слово void . Казалось бы, что раз функция ничего не возвращает, то и не нужно ничего писать. Раньше, кстати, в языке Си так и было сделано, но потом для единообразия всё-таки добавили. Сейчас современные компиляторы будут выдавать предупреждения/ошибки, если вы не укажете тип возвращаемого значения.
В некоторых языках программирования функции, которые не возвращают никакого значения, называют процедурами (например, pascal). Более того, для создания функций и процедур предусмотрен различный синтаксис. В языке Си такой дискриминации нет.
После типа возвращаемого значения записывается имя функции. Ну а уж после имени указываются типы и количество аргументов, которые передаются в функцию.
Давайте посмотрим на заголовки уже знакомых нам функций.
// функция с именем srand, принимающая целое число, ничего не возвращает void srand(int) //функция с именем sqrt, принимающая вещественное число типа float, возвращает вещественное число типа float float sqrt(float) //функция с именем rand, которая не принимает аргументов, возвращает целое число int rand(void) //функция с именем pow, принимающая два аргумента типа double, возвращает вещественное число типа double double pow(double, double)
Как создать свою функцию
Для того чтобы создать свою функцию, необходимо её полностью описать. Тут действует общее правило: прежде чем использовать – объяви и опиши, как должно работать. Для этого вернёмся к схеме структуры программы на языке Си, которая у нас была в самом первом уроке. Отметим на ней те места, где можно описывать функции.
Рис.1 Уточнение структуры программы. Объявление функций.
Как видите, имеется аж два места, где это можно сделать.
Давайте посмотрим на пример, который иллюстрируют создание пользовательской функции вычисления максимального из двух чисел.
#include // объявляем пользовательскую функцию с именем max_num // вход: два целочисленных параметра с именами a и b // выход: максимальное из двух аргументов int max_num(int a, int b) < int max = b; if (a >b) max = a; return max; > //основная программа int main(void)
Давайте я подробно опишу, как будет работать эта программа. Выполняется тело функции main . Создются целые переменные x , y и m . В переменные x и y считываются данные с клавиатуры. Допустим мы ввели 3 5 , тогда x = 3 , y = 5 . Это вам всё и так должно быть понятно. Теперь следующая строчка
m = max_num(x,y);
Переменной m надо присвоить то, что находится справа от знака = . Там у нас указано имя функции, которую мы создали сами. Компьютер ищет объявление и описание этой функции. Оно находится выше. Согласно этому объявлению данная функция должна принять два целочисленных значения. В нашем случае это значения, записанные в переменных x и y . Т.е. числа 3 и 5 . Обратите внимание, что в функцию передаются не сами переменные x и y , а только значения (два числа), которые в них хранятся. То, что на самом деле передаётся в функцию при её вызове в программе, называется фактическими параметрами функции.
Теперь начинает выполняться функция max_num . Первым делом для каждого параметра, описанного в заголовке функции, создается отдельная временная переменная. В нашем случае создаются две целочисленных переменных с именами a и b . Этим переменным присваиваются значения фактических параметров. Сами же параметры, описанные в заголовке функции, называются формальными параметрами. Итак, формальным параметрам a и b присваиваются значения фактических параметров 3 и 5 соответственно. Теперь a = 3 , b = 5 . Дальше внутри функции мы можем работать с этими переменными так, как будто они обычные переменные.
Создаётся целочисленная переменная с именем max , ей присваивается значение b . Дальше проверяется условие a > b . Если оно истинно, то значение в переменной max следует заменить на a .
Далее следует оператор return , который возвращает в вызывающую программу (функцию main ) значение, записанное в переменной max , т.е. 5 . После чего переменные a , b и max удаляются из памяти. А мы возвращаемся к строке
m = max_num(x,y);
Функция max_num вернула значение 5 , значит теперь справа от знака = записано 5 . Это значение записывается в переменную m. Дальше на экран выводится строчка, и программа завершается.
Внимательно прочитайте последние 4 абазаца ещё раз, чтобы до конца уяснить, как работает программа.
А я пока расскажу, зачем нужен нижний блок описания функций. Представьте себе, что в вашей программе вы написали 20 небольших функций. И все они описаны перед функцией main . Не очень-то удобно добираться до основной программы так долго. Чтобы решить эту проблему, функции можно описывать в нижнем блоке.
Но просто так перенести туда полностью код функции не удастся, т.к. тогда нарушится правило: прежде чем что-то использовать, необходимо это объявить. Чтобы избежать подобной проблемы, необходимо использовать прототип функции.
Прототип функции полностью повторяет заголовок функции, после которого стоит ; . Указав прототип в верхнем блоке, в нижнем мы уже можем полностью описать функцию. Для примера выше это могло бы выглядеть так:
#include int max_num(int, int); int main(void) < int x =0, y = 0; int m = 0; scanf("%d %d", &x, &y); m = max_num(x,y); printf("max(%d,%d) = %d\n",x,y,m); return 0; >int max_num(int a, int b) < int max = b; if (a >b) max = a; return max; >
Всё очень просто. Обратите внимание, что у прототипа функции можно не указывать имена формальных параметров, достаточно просто указать их типы. В примере выше я именно так и сделал.
Сохрани в закладки или поддержи проект.
Практика
Решите предложенные задачи:
Для удобства работы сразу переходите в полноэкранный режим
Дополнительные материалы
- пока нет
Функции
Теги: Функции в си, прототип, описание, определение, вызов. Формальные параметры и фактические параметры. Аргументы функции, передача по значению, передача по указателю. Возврат значения.
Введение
Ч ем дальше мы изучаем си, тем больше становятся программы. Мы собираем все действия в одну функцию 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
Всё ещё не понятно? – пиши вопросы на ящик