/ it Новости / Как работают функции в программах? Детальный обзор

Как работают функции в программах? Детальный обзор

Как работают функции в программах? Детальный обзор

1 176 · 18 ноября 2018 в 11:14 ·
Методы или же функции бывают с разными параметрами, данными, а главное вызовами. Новичку порой сложно понять как все это работает, но мы поможем разобраться.

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

Возвращаемые типы

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


В большинстве языков программирования (например, в Java) вы возвращаете значение через ключевое слово return . Но что это означает, и зачем нужно что-то возвращать? А куда вообще это что-то возвращается?



Данная концепция основана на идее о том, что вызов метода – это передача управления программой. При вызове метода (например, методом addNumbers) осуществляется переход к нужной области. Вы говорите: перейти в ту область, для которой задается addNumbers. Когда вы пишете return, то это означает возврат в то место, откуда выполнялся последний вызов метода. Когда вы пишите value, то просите добавить это значение туда, откуда выполняется вызов метода.


Пример:

public class MethodCall1 {

	public static void main(String[] args){
		int x = 5;
		int y = 2;
		int z = 4;
    
		addNumbers(x, y);
		int res = addNumbers(y, z);
    
		System.out.print("The result of y + z is: " + res);
	}
  
	public static int addNumbers(int a, int b) {
		int res = a + b;
		return res;
	}
  
}

Рис 1: Пример MethodCall1 показывает простой вызов метода


В классе MethodCall1 есть три первых переменных: x, y, z, которые заданы целыми числами. Вызываем метод addNumbers с параметрами  x и  y.  addNumbers вычисляет результат и возвращает его значение. Когда метод доходит до оператора return, он уже знает, откуда вызывается, и возвращается туда с конечным значением. К сожалению, в коде выше не был задан тип возвращаемого значения метода, поэтому результат не сохранился.


В следующей строке опять выполняется вызов, но уже с другими параметрами – y и z. Происходит все то же самое, и при достижении ключевого слова return, вызов четко знает, что идет из строки 9 и возвращается туда же. В этот раз результату присвоена переменная res


Внимание: res в данном примере совершенно независима от res, заданной в addNumbers. Это две локальные переменные, которым можно присвоить любое имя, что  никак не отразится на выполнении кода.

Передача параметров по значению

Так что же такое «передача параметров»? Параметром называется значение переменной, которое копируется в вызываемый метод. И этот метод затем может присвоить значению любое имя.


Например, в примере 1 название параметров: a и b. Однако мы называем их x, y, и z. Как это работает? Откуда метод узнает, какое имя необходимо присвоить? 


Существует простой алгоритм, которому следует большинство языков программирования:

  1. Замена параметров в месте вызова. Это значит, что если параметром является переменная, то в нее добавляется значение. И при вызове метода в строке 8 данная переменная становится addNumbers(5,2).


  1. Копирование переменных и присваивание их локальным переменным. Происходит все в так называемом стеке вызовов. То есть вы как будто добавляете для каждого параметра в каждой строке следующее выражение:
    int a = 5;
    int b = 2;

    Возникает вопрос: откуда метод узнает, что есть что. Для этих целей используется порядок отображения. Сигнатура метода addNumbers(int a, int b) означает, что сначала передается а, потом b. Так что все, что стоит в начале, – это а, а все последующее – это b.

Передача параметров по ссылке

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


Переменные в Java могут содержать значения или объекты. Разница между ними обычно не важна. Однако без нее невозможно понять общий принцип передачи параметров: при передаче объекта передается ссылка на объект, а не сам объект. Чтобы лучше во всем этом разобраться, необходимо изучить основы распределения памяти внутри программ.


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


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


При передаче параметров по ссылке вместо копирования самого объекта (см. примеры с целыми числами выше), запоминается адрес объекта.  В качестве примера можно рассмотреть массив в Java. Если параметром выступает массив, то копируется не сам массив, а только его адрес. Это означает, что если вы изменяете массив в вызываемом методе, то как бы заходите в нужный дом и изменяете исходный массив.


Пример:

 

public class Main {

	public static void main(String[] args){
		int[] xs = new int[]{5,4,6};
		int[] ys = new int[]{1,2,3};
    
		addNumbers(xs, ys);
		addNumbers(xs, ys);
    
		for (int i = 0; i< xs.length;i++) 
			System.out.println(xs[i]);
	}
  
	public static void addNumbers(int[] as, int[] bs) {
		for (int i = 0; i< as.length;i++)
			as[i] = as[i]+bs[i];
	}
  
}

Пример 2: Вызов по ссылке в MethodCall2.java


В данном случае объявляются две переменные: xs и ys. Это два массива, каждый из которых состоит из трех целых чисел [4-5]. Затем дважды вызывается метод addNumbers - в качестве параметров передаются два массива [7–8]. В конце каждый элемент xs выводится в отдельной строке [10–12].


В методе addNumbers оба массива представлены в виде параметров as и bs. Но здесь алгоритм передачи параметров немного другой:

  1. Значения все также заменяются, однако в этот раз передается адрес. В псевдокоде он выглядит так: addNumbers(addressof(xs), addressof(ys)), то есть параметрами массивов становятся их адреса. Помните, что адрес параметра похож на адрес дома – это просто какое-то число, которое определяет местоположение чего-либо.
  2. Двум переменным as и bs присваивается адрес:
    int[] as = addressof(xs)
    int[] bs = addressof(ys)


То есть при записи as[i] = as[i] + bs[i]; в действительности происходит следующее:

  1. Справа: поиск того, что задано адресом as[i] и bs[i]. Их совместное добавление.
  2. Слева: повторный переход к адресу as[i] и присвоение ему значения из части справа.


Обратите внимание, что метод addNumbers является void, то есть он не возвращает значений. В нем отсутствует даже явный оператор return. Тем не менее, он не явно присутствует (и не прописывается!) в конце кода и позволяет возвращаться туда, откуда вызывается метод. Добавить метод в код можно после } в строке 21.


Больше интересных новостей