Главная > Уроки > Передача аргументов в функцию

Записывайся на этот курс на Stepike!

Передача аргументов в функцию

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

Рассмотрим пример. Допустим, мы хотим написать программу, которая меняет местами значения переменных. Кажется, всё просто.

Листинг 1.

#include <stdio.h>

void swap(int a, int b){
  int temp;

  temp = a;
  a = b;
  b = temp;
}

int main(void) {
  int x = 4, y = 9;

  printf("x=%d y=%d\n",x,y);
  swap(x,y);
  printf("x=%d y=%d\n",x,y);

  return 0;
}

Вроде бы всё должно работать, но нет. Запустите программу и убедитесь в этом. И дело, как видите, не в функции, функция работает правильно, и значения переменных a и b действительно меняются, но к нашим исходным переменным это не имеет никакого отношения, т.к. они в функцию не передавались, а передавались только их значения.

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

Напомню, что указатель – это такая переменная, в которой хранится адрес в памяти. В зависимости от типа указателя по этому адресу хранится значение соответствующего типа. Перепишем нашу программу используя передачу аргумента по ссылке.

Листинг 2.

#include <stdio.h>

// функция swap принимает два указателя на целые числа
// другими словами, два адреса в памяти компьютера,
// в которых записаны целые числа
void swap(int *a, int *b){ 
// a -- адрес первого числа
// b -- адрес второго числа
  
  int temp;
// *a -- разыменование указателя
// *a позволяет обращаться к значению, хранящемуся по адресу a
// выводим значения, хранящиеся по адресам a и b 
  printf("a=%d b=%d\n",*a,*b);

// переменной temp присваиваем значение, хранящееся по адресу a
  temp = *a;    
// в ячейку по адресу a записываем значение, хранящееся по адресу b
  *a = *b;
// в ячейку по адресу b записываем значение из переменной temp
  *b = temp;
  printf("a=%d b=%d\n",*a,*b);
}

int main(void) {
  int x = 4, y = 9;

  printf("x=%d y=%d\n",x,y);
// передаём адреса переменных x и y в фунцию swap
  swap(&x,&y);

  printf("x=%d y=%d\n",x,y);

  return 0;
}

Программа снабжена подробными комментариями, но я ещё раз поясню, что происходит. В начале программы объявляются переменные x и y. Им присваиваются значения 4 и 9 соответственно. Адреса этих переменных передаются в функцию swap. Обращаю особое внимание. Передаются адреса переменных, а не их значения. Внутри функции, используя дополнительную переменную, значения по переданным адресам меняются местами. Функция swap заканчивает свою работу.

Передача массивов в функцию

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

Листинг 3.

#include <stdio.h>

void print_arr(int arr[], int n){

  for (int k=0; k<n; k++){
    printf("arr[%d] = %d\t", k, arr[k]);
  }

  printf("\n");
}

void print_arr2(int arr[][5], int k, int n){

  for (int i=0; i<k; i++,printf("\n"))
    for(int j=0; j<n; j++)
      printf("arr[%d][%d] = %d\t", i, j, arr[i][j]);
  
  printf("\n");
}


int main(void){
  
  int q[5] = {5,4,2,3,4};
  int q2[2][5] = { {1,2,3,4,5}, {0,9,8,7,6} };

  print_arr(q,5);

  print_arr2(q2,2,5);
  
  return 0;
}

Первый момент. Если в функцию предполагается передавать массив, то необходимо об этом предупредить компилятор. Для этого в заголовке функции при её описании необходимо рядом с именем переменной-массива указать пару квадратных скобок []. Посмотрите на объявление функции print_arr: из него сразу понятно, что переменная arr будет массивом, т.к. рядом с её именем указаны [].

Листинг 4.

void print_arr(int arr[], int n){
  ...
}

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

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

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

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

Сохрани в закладки или поддержи проект.

Практика

Решите предложенные задачи:

Для удобства работы сразу переходите в полноэкранный режим

Дополнительные материалы

  1. пока нет

Оставить комментарий

Чтобы код красиво отображался на странице заключайте его в теги [code] здесь писать код [/code]

Комментарии

Dundukov
"Четвертый нюанс. Массивы всегда передаются в функцию по ссылке. Это означает, что любые изменения массива внутри функции отразятся на исходном массиве."

Значит, никогда с массивыми не нужно работать, как с массивами через * для объявления?
Леха
Я такое стандартное объяснение передачи аргументов по указателю никогда не мог понять (умышленно говорю передача "по укозателю", потому что в С++ есть отдельное понятие ссылки и "передача по ссылке" там имеет совсем другой смысл). Альтернативное объяснение такое: когда мы передаём значение по указателю в функцию, она по прежнему берет переданные аргументы по-значению (копируя их к себе в стек). Только в этом случае, это уже копии указателей на оригинальные переменные!!! Как и в случае с обычными переменными любое изменение копий указателей в функции не повлияет на оригинальные переменные. Но ведь эти копии указателей по прежнему указывают на наши оригинальные значения переменных!!! Т.е. если их разименовать мы получим доступ к значениям переменных и даже сможем их изменить!!! В этом и заключается трюк передачи указателей вместо самих переменных. Это как троянский конь, сами значения указатели вроде бы скопированы и вроде безобидны, но фокус то в том что даже эти копии по прежнему указывают на оригинальные переменные. Остаётся только их разименовать!!!