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

Как устроены функции

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

Листинг 1.

int main(void){  // заголовок функции

// в фигурных скобках записано тело функции

}

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

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

Сначала записывается тип возвращаемого значения, например, int, как в функции main. Если функция не должна возвращать никакое значение в программу, то на этом месте пишется ключевое слово void. Казалось бы, что раз функция ничего не возвращает, то и не нужно ничего писать. Раньше, кстати, в языке Си так и было сделано, но потом для единообразия всё-таки добавили. Сейчас современные компиляторы будут выдавать предупреждения/ошибки, если вы не укажете тип возвращаемого значения.
В некоторых языках программирования функции, которые не возвращают никакого значения, называют процедурами (например, pascal). Более того, для создания функций и процедур предусмотрен различный синтаксис. В языке Си такой дискриминации нет.

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

Давайте посмотрим на заголовки уже знакомых нам функций.

Листинг 2.

// функция с именем srand, принимающая целое число, ничего не возвращает
void srand(int)

//функция с именем sqrt, принимающая вещественное число типа float, возвращает вещественное число типа float
float sqrt(float)

//функция с именем rand, которая не принимает аргументов, возвращает целое число
int rand(void)

//функция с именем pow, принимающая два аргумента типа double, возвращает вещественное число типа double
double pow(double, double)

Технология Proof-of-Work

В майнинг технология Proof-of-Work (доказательство работой) пришла вместе с Bitcoin и применяется для разгадывания блока (майнинга) с помощью специального оборудования.

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

Если решения совпали – за выполненную работу майнер получает вознаграждение, при этом размер вознаграждения фиксированный (например, за разгадывание блока Bitcoin майнер получит 12,5 btc).

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

особенности Proof-of-Work

Интересно. Технология PoW существовала задолго до появления Bitcoin: в 1993 году в научной статье, авторами которой были Синтия Двор и Мони Наор. Они не дали название технологии, а лишь предложили концепцию: для доступа к какому-либо абстрактному ресурсу нужно решить определенную задачу. Идею подхватил Адам Блэк, запустивший в 1997 году проект Hashcash, основная задача которого заключалась в защите ресурса от спама. Через 2 года появился термин Proof of Work в научной статье Маркуса Якобсена и Ари Джуэлса.

Технология Proof-of-Stake

В качестве альтернативы PoW в майнинге была создана технология (доказательство ставкой).

Для майнинга по PoS не нужно специальное оборудование. Вам достаточно иметь кошелек и некоторое количество монеты, которая добывается по данной технологии.

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

Однако минус этой технологии в том, что преимущество в подтверждении транзакций получает тот участник, у которого количество монет в кошельке больше. Но и держатели небольшого количества монет принимают участие в разгадывании блока пропорционально количеству своих монет: если у майнера 1% монет от их общего числа, то количество блоков, которые он разгадает, будет равно 1%.

Proof-of-Stake против Proof-of-Work

Начисление вознаграждения при разгадывании блока при PoS иное, чем при PoW: майнер получает комиссию за проведение транзакции (то есть вознаграждение начисляется из монет, которые уже находятся в обороте), а не установленное количество монет, то есть при PoS эмиссия криптовалюты не происходит. Эта сумма значительно меньше, чем при майнинге по PoW технологии, но и затраты на PoS значительно меньше.

Как создать свою функцию

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

Рис.1 Уточнение структуры программы. Объявление функций.

Как видите, имеется аж два места, где это можно сделать.

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

Листинг 3.

#include <stdio.h>

// объявляем пользовательскую функцию с именем max_num
// вход: два целочисленных параметра с именами a и b 
// выход: максимальное из двух аргументов
int max_num(int a, int b){
  int max = b;
  if (a > b) 
    max = a;

  return max;
}

//основная программа
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;
}

Давайте я подробно опишу, как будет работать эта программа.
Выполняется тело функции main. Создются целые переменные x, y и m.
В переменные x и y считываются данные с клавиатуры. Допустим мы ввели 3 5, тогда x = 3, y = 5. Это вам всё и так должно быть понятно. Теперь следующая строчка

Листинг 4.

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 удаляются из памяти. А мы возвращаемся к строке

Листинг 5.

m = max_num(x,y);

Функция max_num вернула значение 5, значит теперь справа от знака = записано 5. Это значение записывается в переменную m.
Дальше на экран выводится строчка, и программа завершается.

Внимательно прочитайте последние 4 абазаца ещё раз, чтобы до конца уяснить, как работает программа.

А я пока расскажу, зачем нужен нижний блок описания функций. Представьте себе, что в вашей программе вы написали 20 небольших функций. И все они описаны перед функцией main. Не очень-то удобно добираться до основной программы так долго. Чтобы решить эту проблему, функции можно описывать в нижнем блоке.

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

Прототип функции полностью повторяет заголовок функции, после которого стоит ;. Указав прототип в верхнем блоке, в нижнем мы уже можем полностью описать функцию. Для примера выше это могло бы выглядеть так:

Листинг 6.

#include <stdio.h>

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;
}

Всё очень просто

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

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

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

Adblock
detector