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

Последнее обновление: 22.01.2024

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

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

#include <stdio.h>

void increment(int x)
{
	x = x + 1;
	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, использовав в качестве параметра указатель:

#include <stdio.h>

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

int main(void)
{
	int n = 10;
	increment(&n);
	printf("main function: %d \n", n);
	return 0;
}

Теперь в функции increment разыменовываем указатель, получаем его значение и увеличиваем его на единицу.

*x = *x + 1;

Это изменяет значение, которое находится по адресу, хранимому в указателе x.

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

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

increment function: 11
main function: 11

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

#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, и в итоге их значения будут изменены.

Константые параметры

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

#include <stdio.h>

// константный параметр
int twice(const int *x)
{
	//*x = *x * *x;     // так нельзя, так как x - константный параметр
    int y = *x + *x;
	return y;
}

int main(void)
{
	int n = 10;
	int m = twice(&n);
	printf("n = %d \n", n);     // n = 10
	printf("m = %d \n", m);     // m = 20
	return 0;
}

Фактически такой константный параметр будет работать как указатель на константу - мы не сможем изменить его значение внутри функции.

Массивы в параметрах

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

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

#include <stdio.h>

void twice(size_t n, int p[])
{
	for(size_t i = 0; i < n; i++)
	{
		p[i]= p[i] * 2;
	}
}
int main(void)
{
	int nums[] = {1, 2, 3, 4, 5};
	// получаем количество элементов массива
	size_t length = sizeof(nums)/sizeof(nums[0]);
	
	twice(length, nums);
	
	for(size_t i=0; i<length; i++)
	{
		printf("%d \t", nums[i]);
	}
	
	return 0;
}

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

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

2	4	6	8	10

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

#include <stdio.h>

void twice(size_t n, int *p)
{
	for(size_t i=0; i<n; i++)
	{
		*p= *p * 2;	// увеличиваем значение по указателю
		p = p + 1; // перемещаем указатель на следующий элемент
	}
}
int main(void)
{
	int nums[] = {1, 2, 3, 4, 5};
	size_t length = sizeof(nums)/sizeof(nums[0]);
	
	twice(length, nums);
	
	for(size_t i=0; i<length; i++)
	{
		printf("%d \t", nums[i]);
	}
	
	return 0;
}

В итоге в данном случае не будет большой разницы, какой тип имеет параметр - массив или указатель. Компилятор в любом случае будет рассматривать параметр типа int array[] как указатель int* array

При определении параметра в принципе можно даже указать длину массива, которая будет представлять минимально допустимое количество элементов:

#include <stdio.h>
 
// функция ожидает получить массив как минимум из 4 элементов
void twice(int numbers[4])
{
	for(size_t i = 0; i < 4; i++)
	{
		numbers[i]= numbers[i] * 2;
        printf("%d \t", numbers[i] * 2);
	}
}
int main(void)
{
	int nums[5] = {1, 2, 3, 4, 5};
	twice(nums);
	return 0;
}

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

Начиная со стандарта C99 Си позволяет установить минимальную длину массива с помощью оператора static:

// ожидаем массив как минимум с 4 элементами
void twice(int numbers[static 4])

Константный массив

Массив также можно передавать как константный - в этом случае значения его элементов нельзя изменить:

#include <stdio.h>
 
// массив p - константный
void twice(size_t n, const int p[])
{
	for(size_t i = 0; i < n; i++)
	{
		// p[i]= p[i] * 2;  // Так нельзя - массив константный
        printf("%d \t", p[i] * 2);
	}
}
int main(void)
{
	int nums[] = {1, 2, 3, 4, 5};
	// получаем количество элементов массива
	size_t length = sizeof(nums)/sizeof(nums[0]);
	twice(length, nums);
	return 0;
}
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850