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

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

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

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

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

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

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

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

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


ТипВозвращаемогоЗначения ИмяФункции(СписокФормальныхАргументов){
   ТелоФункции;
  ...
  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 не обязательно должен находиться в конце тела функции.

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

return;

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

#include <stdio.h>
int sum(int x, int y) {
  int k;
  k = x + y;
  return(k);
}
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);
}

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

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

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

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

Пример

#include <stdio.h>
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);
}

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

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

#include <stdio.h>
int fact(int num) {
 if(num==1)   return(1);
 else return(num*fact(num-1));  // рекурсивный вызов
}
int main() {
 int a, r;
 printf("a= ");
 scanf("%d",&a);
 r = fact(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)
Cинус x
double sinh(double x)
Cинус гиперболический x
double sqrt(double x)
Квадратный корень x
double tan(double x)
Тангенс x
double tanh(double x)
Тангенс гиперболический x

Назад


Назад: Язык Си

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

  • Валерий

    Елена, добрый день.

    Мне попался примерно такой программный текст на Си:
    Основная программа:

    func(0,1);      // Вызов функции func()

    Функция:
    func(bool x=TRUE, bool y=TRUE)   { // удивительно то, что формальным параметрам задают значения

    }

    При этом программа работает.

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


    • Елена Вставская

      Здравствуйте, Валерий!
      Действительно, формальным аргументам функции могут быть присвоены значения по умолчанию. Эти значения будут использоваться в том случае если они не переданы в функцию при вызове.
      Например, в Вашем случае можно вызвать функцию
      func(0); // равносильно вызову func(0,1);
      или
      func(); // равносильно вызову func(1,1);
      То есть если в функции есть значения по умолчанию, то значения фактических параметров для них могут не передаваться.
      Но значения по умолчанию могут иметь только формальные параметры, находящиеся справа в перечне параметров. Иными словами, если для аргумента задано значение по умолчанию, то правее его аргумент без заданного значения по умолчанию стоять не может.
      Также некорректным будет вызов функции
      func(,0); // компилироваться не будет

      Это свойство стало доступно в языке C++, но не было доступно в Си.
      Подробнее смотрите здесь.


  • Добрый день.
    При изучении языка Си у меня возник вопрос, каким образом написаны функции стандартных библиотек. К примеру, функции ввода-вывода. При помощи только операторов языка Си, или еще, что-то используется, например другие функции. Тогда как эти функции созданы? Или есть какие-то первичные функции, которые написаны при помощи основных примитивов языка Си (операторы, определение переменных и т.д. без применения каких либо функций)?
     
    Можно ли например увидеть код функции printf(), или getch()?


  • Алексей

    void main() и int main(void) дадут одинаковый результат? В обоих случаях не будет возвращаться результат?


    • Елена Вставская

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


  • Алексей

    Добрый день!

    Елена, подскажите плз, возможен ли вариант double main(), float main(), char main(), string main() и т.д.?

    И в чём будет практическое отличие, например, int main() и int main(void)? Как это отобразиться на финальном результате программы?


    • Елена Вставская

      Тип возвращаемого значения функции main() — это значение, возвращаемое ей операционной системе. Операционная система анализирует целочисленное значение, поэтому используется
      int main() {…}
      Возможен также вариант функции main() не возвращающей значение. При этом ее можно записать как
      void main() {…}
      В этом случае последняя команда return 0; в функции main() отсутствует.
      Насчет аргументов, которые могут передаваться в функцию main() можно посмотреть здесь.


  • 3D Mountain Bike

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


    • Елена Вставская

      Согласна отчасти. В языке Си нет требования, чтобы определение функции было до вызова. Но это требование среды разработки Visual Studio. Для того, чтобы в Visual Studio программа корректно откомпилировалась, до вызова функции должна быть описана хотя бы сигнатура функции. В противном случае при компиляции возникнет ошибка.

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

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


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

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