Способ передачи параметров функции c

Функции

Введение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Читайте также:  Какие существуют способы расположения заголовков

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источник

BestProg

Функции. Часть 1. Описание функции. Фактические и формальные параметры. Передача параметров в функцию по значению и по адресу. Прототип функции

Содержание

Поиск на других ресурсах:

1. Что такое функция? Определение функции. Преимущества использования функций

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

Читайте также:  Как избавиться от мешков под глазами домашний способ

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

Функция – это часть программы, которая имеет следующие свойства или признаки:

  • есть логически самостоятельной частью программы;
  • имеет имя, на основании которого осуществляется вызов функции (выполнение функции). Имя функции должно соответствовать правилам создания идентификаторов языка C++;
  • может содержать список параметров, которые передаются ей для обработки или использования. Если функция не содержит списка параметров, то такая функция называется функцией без параметров;
  • может возвращать (не обязательно) некоторое значение. В случае, если функция не возвращает никакого значения, тогда указывается ключевое слово void ;
  • имеет собственный программный код, который расположен между фигурными скобками < >. Программный код решает задачу, поставленную на эту функцию. Программный код функции, реализованный в фигурных скобках, называется «тело функции».

Использование функций в программах дает следующие преимущества:

  • компактная организация программы путем удобного вызова программного кода по его имени, который в программе может встречаться несколько раз (повторяться);
  • экономия памяти, размера исходного и исполнительного кода и т.д.;
  • уменьшение риска возникновения ошибок для больших наборов кодов;
  • повышение читабельности программного кода.
2. Какая общая форма описания функции?

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

  • тип– тип значения, возвращаемого функцией. Если поле « тип » содержит ключевое слово void , то функция не возвращает никакого значения;
  • имя_функции – это непосредственно имя функции. По этому имени осуществляется вызов программного кода, реализованного в теле_функции . Кроме того, имя_функции есть указателем на эту функцию . Значением указателя есть адрес точки входа в функцию;
  • список_параметров – параметры, которые передаются в функцию. Функция может получать любое число параметров. Если описывается функция без параметров, то в скобках указывается ключевое слово void ;
  • тело_функции – набор операторов программного кода, реализующих алгоритм вычисления внутри функции;
  • return (выражение) – ключевое слово return указывает, что функция возвращает значение заданное в (выражение) . Слово return может встречаться в нескольких местах тела функции в зависимости от алгоритма (повторяться).
3. Примеры описания и использования функций, которые не возвращают значения

Если функция не возвращает значения, тогда она должна начинаться из ключевого слова void .

Пример 1. Функция MyFunc1() без параметров, которая не возвращает значения.

Если в теле некоторого класса или модуля описать функцию:

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

Пример 2. Функция MyFuncMult2() , которая получает один параметр целого типа и не возвращает значения. Функция осуществляет умножение полученного параметра на 2 и выводит результат на форму в элементе управления label1 .

Вызов функции из другого программного кода:

Пример 3. Функция, которая получает 2 параметра типа double , находит их произведение и выводит его на форму в элементе управления label1 .

Вызов функции из другого программного кода:

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

Пример 1. Функция, которая получает один параметр целого типа, умножает его на 5 и возвращает результат. Функция не выполняет вывода результата.

Вызов функции из другого программного кода

Пример 2. Функция вычисления значения y = sign(x) , определяющегося по правилу:

Реализация функции:

Вызов функции из другого программного кода:

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

Пример 1. Пример функции MaxFloat() , получающей 2 параметра типа float и возвращающей максимум из них.

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

Вызов функции MaxFloat() из другого программного кода:

Пример 2. Функция MaxInt3() , которая находит максимальное значение между тремя целыми числами.

Вызов функции из другого программного кода

6. Какие существуют способы передачи параметров в функцию? Пример

В C++ существует 3 способа передачи параметров в функцию:

  • передача параметра по значению ( Call-By-Value ). Это простая передача копий переменных в функцию. В этом случае изменение значений параметров в теле функции не изменит значений, которые передавались в функцию извне (при ее вызове);
  • передача параметра по адресу переменной. В этом случае функции в качестве параметров передаются не копии переменных, а копии адресов переменных, то есть указатель на переменную. Используя этот указатель функция осуществляет доступ к нужным ячейкам памяти где расположена передаваемая переменная и может изменять ее значение. Общие сведения об указателях приведены здесь ;
  • передача параметра по ссылке ( Call-By-Reference ). Передается ссылка (указатель) на объект (переменную), что позволяет синтаксически использовать эту ссылку как указатель, и как значение. Изменения, внесенные в параметр, который передается по ссылке, изменяют исходную копию параметра вызывающей функции.
Читайте также:  Дрожжевое тесто холодным способом для пиццы

Пример. Этот пример демонстрирует отличие между передачей параметров по значению, по адресу и по ссылке. Описывается функция, которая получает три параметра. Первый параметр ( x ) передается по значению. Второй параметр ( y ) передается по адресу (как указатель). Третий параметр ( z ) передается по ссылке.

Демонстрация вызова функции MyFunction() из другого программного кода:

Как видно из результата, значение переменной a не изменилось. Переменная a передавалась в функцию MyFunction() с передачей значения (первый параметр).

Однако, значение переменной b после вызова функции MyFunction() изменилось. Это связано с тем, что в функцию MyFunction() передавалось значение адреса переменной b . Имея адрес переменной b в памяти компьютера, внутри функции MyFunction() можно изменять значения этой переменной с помощью указателя y .

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

7. Что такое формальные и фактические параметры функции? Пример

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

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

Пример. Функция MyAbs() , находящая модуль числа имеет один формальный параметр x .

Вызов функции из другого программного кода (другой функции)

При вызове функции из другого программного кода имеет место фактический параметр. В данном примере фактический параметр это переменная a и константа 23.

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

Поскольку, фактические параметры копируются в стек, то изменение значений формальных параметров в теле функции не изменит значений фактических параметров (так как это есть копия фактических параметров).

8. Какая область видимости формальных параметров функции? Пример

Область видимости формальных параметров функции определяется границами тела функции, в которой они описаны. В приведенном ниже примере формальный параметр n целого типа имеет область видимости в границах фигурных скобок < >.

Пример. Функция, которая вычисляет факториал целого числа n .

Вызов функции из другого программного кода (другой функции):

9. Что такое прототип функции? Пример

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

Прототип функции содержит:

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

Общая форма объявления прототипа функции следующая:

  • return_type – тип, возвращаемый функцией;
  • FuncName – имя функции;
  • parameters – параметры функции.

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

можно указать следующий прототип

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

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

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

Пусть заданы 2 функции с именами A() и B () . Здесь важен порядок объявления и порядок вызова. Если из функции B() вызвать функцию A() , объявление и тело которой реализованы после функции B() , как показано ниже

то компилятор выдаст сообщение об ошибке

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

Источник

Оцените статью
Разные способы