Указатели в параметрах функции


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

void increment(int x)
{
x++;
printf("increment function: %d \n", x);
}

int main(void)
{
int n = 10;
increment(n);
printf("main function: %d \n", n);
return 0;
}
Здесь переменная n передается в качестве аргумента для параметра x. Передача происходит по значению, поэтому любое изменение параметра x в функции increment никак не скажется на значении переменной n. Что мы можем увидеть, запустим программу:

increment function: 11
main function: 10
Теперь изменим функцию increment, использовав в качестве параметра указатель:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

void increment(int *x)
{
(*x)++;
printf("increment function: %d \n", *x);
}

int main(void)
{
int n = 10;
increment(&n);
printf("main function: %d \n", n);
return 0;
}
Для изменения значения параметра применяется операция разыменования с последующим инкрементом: (*x)++. Это изменяет значение, которое находится по адресу, хранимому в указателе x.

Поскольку теперь функция в качестве параметра принимает указатель, то при ее вызове необходимо передать адрес переменной: increment(&n);.

В итоге изменение параметра x также повлияет на переменную n:

increment function: 11
main function: 11
Еще один показательный пример применения указателей в параметрах - функция обмена значений:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b=temp;
}
int main(void)
{
int x = 100;
int y = 200;
swap(&x, &y);
printf("x=%d \t y=%d \n", x, y);
return 0;
}
Функция swap() в качестве параметров принимает два указателя. Посредством переменной temp происходит обмен значениями.

При вызове функции swap в нее передаются адреса переменных x и y, и в итоге их значения будут изменены.

Массивы в параметрах
Если функция принимает в качестве параметра массив, то фактически в эту функцию передается только адрес начала массива. То есть как и в случае с указателями нам доступен адрес, по которому мы можем менять значения. В отличие от параметров примитивных типов, которые передаются по значению.

Например, определим функцию для увеличения элементов массива в два раза:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

void twice(int n, int p[])
{
for(int i=0; i<n; i++)
{
p[i] *=2;
}
}
int main(void)
{
int nums[] = {1, 2, 3, 4, 5};
int length = sizeof(nums)/sizeof(nums[0]);

twice(length, nums);

for(int i=0; i<length; i++)
{
printf("%d \t", nums[i]);
}

return 0;
}
Функция twice в качестве параметров принимает массив и число его элементов и в цикле увеличивает их в два раза.

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

2 4 6 8 10
Так как передача массива в функцию фактически представляет передачу адреса первого элемента, то массивы в параметрах мы можем заменить указателями:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

void twice(int n, int *p)
{
for(int i=0; i<n; i++)
{
*p++ *= 2;
}
}
int main(void)
{
int nums[] = {1, 2, 3, 4, 5};
int length = sizeof(nums)/sizeof(nums[0]);

twice(length, nums);

for(int i=0; i<length; i++)
{
printf("%d \t", nums[i]);
}

return 0;
}
С помощью выражения *p++ *= 2 получаем значение по адресу из указателя, увеличиваем его в два раза и перемещаем указатель на один элемент вперед. В итоге в данном случае не будет большой разницы, какой тип имеет параметр - массив или указатель.