Как передать ссылку на массив в функцию c
Перейти к содержимому

Как передать ссылку на массив в функцию c

  • автор:

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

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

Это можно сделать по-разному, но результат будет одинаковый:

  • void some_function(int array[]);
  • void some_function(int *array);

При этом обязательно нужно указать тип элемента массива.

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

void some_function(int array[], int number_of_elements);

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

show_array.c

void show_array (int array [], int number_of_elements)
for ( int i = 0; i < number_of_elements; i++) printf("%d\t", array[i]);
>
printf(«\n»);
>

int main()
int little_numbers[5] = ;
int big_numbers[3] = ;
show_array(little_numbers, 5);
show_array(big_numbers, 3);
>

Массив просто передается в функцию по имени (а его имя — это адрес первого элемента), а также указывает параметр, который сообщает функции количество элементов, содержащихся в массиве:

Изменение массива из функции

Возможно ли поменять значения элементов из функции?

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

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

values_from_keyboard.c

#include
void read_array(int array[], int number_of_elements)
for(int i = 0; i < number_of_elements; i++)
printf(«Введите значение №%d: «, i);
scanf(«%d», &array[i]);
>
>

int main()
int numbers[3] = ;
read_array (numbers, 3); //массив будет изменен!
printf(» Значения массива\n»);
for (int i = 0; i < 3; i++) printf(" numbers [%d] \n", i);
>

Как видите, программа передает массив в функцию по имени, а функция присваивает массиву элементы.

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

Как передать ссылку на массив в функцию c

Если функция принимает в качестве параметра массив, то фактически в эту функцию передается указатель на первый элемент массива. То есть как и в случае с указателями нам доступен адрес, по которому мы можем менять значения. Поэтому следующие объявления функции будут по сути равноценны:

void print(int numbers[]); void print(int *numbers);

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

#include void print(int[]); int main() < int nums[] ; print(nums); > void print(int numbers[]) < std::cout 

В данном случае функция print выводит на консоль первый элемент массива.

Теперь определим параметр как указатель:

#include void print(int*); int main() < int nums[] ; print(nums); > void print(int *numbers)

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

Ограничения

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

void print(int numbers[]) < int size = sizeof(numbers) / sizeof(numbers[0]); // или так // size_t size = std::size(nums); std::cout

И также мы не сможем использовать цикл for для перебора этого массива:

void print(int numbers[])

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

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

Первый подход заключается в том, чтобы один из элементов массива сам сигнализировал о его окончании. В частности, массив символов может представлять строку - набор символов, который завершается нулевым символом '\0'. Фактически нулевой символ служит признком окончания символьного массива:

#include void print(char[]); int main() < char chars[] ; print(chars); > void print(char chars[]) < for (unsigned i<>; chars[i] != '\0'; i++) < std::cout >

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

#include void print(int[], size_t); int main() < int nums[]; size_t n ; print(nums, n); > void print(int numbers[], size_t n) < for(size_t i <>; i < n; i++) < std::cout >

Третий подход заключается в передаче указателя на конец массива. Можно вручную вычислять указатель на конец массива. А можно использовать встроенные библиотечные функции std::begin() и std::end() :

int nums[] < 1, 2, 3, 4, 5 >; int *begin ; // указатель на начало массива int *end ; // указатель на конец массива

Причем end возвращает указатель не на последний элемент, а адрес за последним элементом в массиве.

Применим данные функции:

#include void print(int*, int*); int main() < int nums[] < 1, 2, 3, 4, 5 >; int *begin ; int *end ; print(begin, end); > void print(int *begin, int *end) < for (int *ptr ; ptr != end; ptr++) < std::cout >

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

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

#include void print(const int*, const size_t); void twice(int*, const size_t); int main() < int numbers[]; size_t n = std::size(numbers); print(numbers, n); twice(numbers, n); // увеличиваем элементы массива в два раза print(numbers, n); > void print(const int numbers[], const size_t n) < for(size_t i <>; i < n; i++) < std::cout std::cout void twice(int *numbers, const size_t n) < for(size_t i <>; i < n; i++) < numbers[i] = numbers[i] * 2; >>

В данном случае функция print просто выводит значения из массива, поэтому параметры этой функции помечаются как константные.

Функция twice изменяет элементы массива - увеличивает их в два раза, поэтому в этой функции параметр-массив является неконстантным. Причем поле выполнения функции twice массив numbers будет изменен.

Консольный вывод программы:

1 2 3 4 5 2 4 6 8 10

Передача массив по ссылке

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

void print(int (&)[]);

Обратите внимание на скобки в записи (&) . Они указывают именно на то, что массив передается по ссылке. Пример использования:

#include void print(int (&)[], size_t); int main() < int nums[] ; size_t count = std::size(nums); print(nums, count); > void print(int (&numbers)[], size_t count) < for(size_t i<>; i < count; i++) < std::cout >

Подобным образом можпо передавать константные ссылки на массивы.

void print(const int (&)[]);

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

#include void print(const int (&)[5]); // массив строго с 5 элементами int main() < int nums1[] ; print(nums1); > void print(const int (&numbers)[5]) < for(unsigned i<>; i < 5; i++) < std::cout >

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

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

int nums2[] ; print(nums2); // ! Ошибка - в массиве nums2 6 элементов

Передача многомерного массива

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

Когда определяется параметр как указатель на массив, размер второй размерности (а также всех последующих размерностей) должен быть определен, так как данный размер является частью типа элемента. Пример объявления:

void print(int (*numbers)[3]);

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

void print(int *numbers[3])

В данном случае параметр определен как массив указателей, а не как указатель на массив.

Рассмотрим применение указателя на массив в качестве параметра:

#include void print(const int(*)[3], const size_t); int main() < int table[][3] < , , >; // количество строк или подмассивов size_t rowsCount ; print(table, rowsCount); > void print(const int (*rows)[3], const size_t rowsCount) < // количество столбцов или элементов в каждом подмассиве size_t columnsCount ; for(size_t i<>; i < rowsCount; i++) < for (size_t j<>; j < columnsCount; j++) < std::cout std::cout >

В функции main определяется двухмерный массив - он состоит из трех подмассивов. Каждый подмассив имеет по три элемента.

В функцию print вместе с массивом передается и число строк - по сути число подмассивов. В самой функции print получаем количество элементов в каждом подмассиве и с помощью двух циклов перебираем все элементы. С помощью выражения rows[0] можно обратиться к первому подмассиву в двухмерном массиве, а с помощью выражения rows[0][0] - к первому элементу первого подмассива. И таким образом, манипулируя индексами можно перебрать весь двухмерный массив.

В итоге мы получим следующий консольный вывод:

1 2 3 4 5 6 7 8 9

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

#include void print(const int[][3], const size_t); int main() < int table[][3] < , , >; // количество строк или подмассивов size_t rowsCount ; print(table, rowsCount); > void print(const int rows[][3], const size_t rowsCount) < // количество столбцов или элементов в каждом подмассиве size_t columnsCount ; for( size_t i<>; i < rowsCount; i++) < for (size_t j<>; j < columnsCount; j++) < std::cout std::cout >

Массивы и функции в языке Си. Передача указателя на массив

Массивы, также как остальные переменные, можно передавать в функции в качестве аргументов. Рассмотрим такую программу:

#include #include #include #define N 10 void rand_fill(int arr[], int min, int max); int main() { int numbers[N]; rand_fill(numbers, 30, 90); for (int i = 0; i  N; i++) printf("%d ", numbers[i]); printf("\n"); } void rand_fill(int arr[], int min, int max) { srand(time(NULL)); for (int i = 0; i  N; i++) arr[i] = rand() % (max - min + 1) + min; }

В теле функции main объявляется массив, состоящий из 10 элементов. Далее вызывается функция rand_fill() , которой передаются в качестве аргументов имя массива и два целых числа.

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

На прошлом уроке мы выяснили, что имя массива — это константный указатель на первый элемент массива; т.е. имя массива содержит адрес. Выходит, что мы передаем в функцию копию адреса, а не копию значения. Как мы уже знаем, передача адреса приводит к возможности изменения локальных переменных в вызывающей функции из вызываемой. Ведь на одну и ту же ячейку памяти могут ссылаться множество переменных, и изменение значения в этой ячейке с помощью одной переменной неминуемо отражается на значениях других переменных.

Описание вида arr[] в параметрах функций говорит о том, что в качестве значения мы получаем указатель на массив, а не обычную (скалярную) переменную типа int , char , float и т.п.

Если в функцию передается только адрес массива, то в теле функции никакого массива не существует, и когда там выполняется выражение типа arr[i] , то на самом деле arr ‒ это не имя массива, а переменная-указатель, к которой прибавляется смещение. Поэтому цикл в функции rand_fill можно переписать на такой:

for (int i = 0; i  N; i++) *arr++ = rand() % (max - min + 1) + min;

В теле цикла результат выражения справа от знака присваивания записывается по адресу, на который указывает arr . За это отвечает выражение *arr . Затем указатель arr начинает указывать на следующую ячейку памяти, т.к. к нему прибавляется единица ( arr++ ). Еще раз: сначала выполняется выражение записи значения по адресу, который содержится в arr ; после чего изменяется адрес, содержащийся в указателе (сдвигается на одну ячейку памяти определенного размера).

Поскольку мы можем изменять arr , это доказывает, что arr — обычный указатель, а не имя массива. Тогда зачем в заголовке функции такой гламур, как arr[] ? Действительно, чаще используют просто переменную-указатель:

void rand_fill(int *arr, int min, int max);

Хотя в таком случае становится не очевидно, что принимает функция ‒ указатель на обычную переменную или все-таки на массив. В любом случае она будет работать.

Часто при передаче в функцию массива туда же через отдельный аргумент передают и количество его элементов. В примере выше N является глобальной константой, поэтому ее значение доступно как из функции main , так и rand_fill . Иначе, можно было бы определить rand_fill так:

void rand_fill(int *arr, int n, int min, int max) { srand(time(NULL)); for (int i = 0; i  n; i++) *arr++ = rand() % (max - min + 1) + min; }

В данном случае параметр n — это количество обрабатываемых элементов массива.

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

int arr_sum(int *arr) { int s = 0, i; for(i = 0; i  N; i++) { s = s + arr[i]; } return s; }

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

int arr_sum(const int *arr);

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

Усложним программу, которая была приведена в начале этого урока:

#include #include #include #define N 10 void rand_fill(int *arr, int min, int max); void arr_inc_dec(int arr[], char sign); void arr_print(int *arr); int main() { int numbers[N], i, minimum, maximum; char ch; printf("Enter minimum & maximum: "); scanf("%d %d", &minimum, &maximum); rand_fill(numbers, minimum, maximum); arr_print(numbers); scanf("%*c"); // избавляемся от \n printf("Enter sign (+, -): "); scanf("%c", &ch); arr_inc_dec(numbers, ch); arr_print(numbers); } void rand_fill(int *arr, int min, int max) { srand(time(NULL)); for (int i = 0; i  N; i++) *arr++ = rand() % (max - min + 1) + min; } void arr_inc_dec(int *arr, char sign) { for (int i = 0; i  N; i++) { if (sign == '+') arr[i]++; else if (sign == '-') arr[i]--; } } void arr_print(int *arr) { printf("The array is: "); for (int i = 0; i  N; i++) printf("%d ", *arr++); printf("\n"); }

Теперь у пользователя запрашивается минимум и максимум, затем создается массив из элементов, значения которых лежат в указанном диапазоне. Массив выводится на экран с помощью функции arr_print() . Далее у пользователя запрашивается знак + или -. Вызывается функция arr_inc_dec() , которая в зависимости от введенного знака увеличивает или уменьшает на единицу значения элементов массива.

В функциях rand_fill и arr_print используется нотация указателей. Причем в теле функций значения указателей меняются: они указывают сначала на первый элемент массива, затем на второй и т.д. В функции arr_inc_dec используется вид обращения к элементам массива. При этом значение указателя не меняется: к arr прибавляется смещение, которое увеличивается на каждой итерации цикла. Ведь на самом деле запись arr[i] означает *(arr+i) .

При использовании нотации обращения к элементам массива программы получаются более ясные, а при использовании записи с помощью указателей они компилируются чуть быстрее. Это связано с тем, что когда компилятор встречает выражение типа arr[i] , он тратит время на преобразование его к виду *(arr+i) .

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

Курс с решением задач:
pdf-версия

Передача массива по ссылке

Author24 — интернет-сервис помощи студентам

Здравствуйте, помогите разобраться с проблемой: Задание состоит в том, чтобы написать две встраиваемые функции сортировки массива с выбором варианта обмена элементов. Первый вариант это передача массива с помощью указателя (это я понял), а вот передача по ссылке никак не получается, получается только передать первый элемент, а вот как дальше перемещаться по массиву не пойму. А в различных темах на форумах разбираются только варианты передачи массива по указателю, а по ссылке везде объясняют, как передаются переменные, а с массивами ничего.Помогите пожалуйста.

Вот что пока получилось:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
#include #include #include void random_mass(int *a,int n); void print_disp(int *a,int n); void sort(int *a,int n); void sort1(int &a,int n); void sort1(int &a,int n) { } main(){ clrscr(); int size; cout"Введите размерность массива: "; cin>>size; int choice; int *a=new int[size]; random_mass(a,size); cout"Массив:\n"; print_disp(a,size); cout"\n\nВыберите варант обмена (0-по ссылке 1-по указателю): "; cin>>choice; if (choice) sort(&a[0],size); ///непонятно else sort(a[0],size); cout"\nОтсортированный массив:\n"; print_disp(a,size); delete [] a; getch(); return 0; } void random_mass(int *a,int n) { randomize(); for (int i=0;in;i++) a[i]=random(100)-50; } void print_disp(int *a,int n) { for (int i=0;in;i++) couta[i]" "; } void sort(int *a,int n) { for (int i = 1; i  n; i++) { int t = a[i]; for (int j = i; j > 0 && a[j-1] > t; j--) a[j] = a[j-1]; a[j] = t; } }

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Передача массива по ссылке
Как правильно передавать двумерный статический массив в мои функции? #include <iostream> #include.

Передача массива в функцию по ссылке
Здравствуйте. Есть функция: void f(int &a) < int *p=&a; p=1; >Но почему-то при.

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

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

Эксперт С++

8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562

Гм. Что-то вроде такого.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include void foo(int& a, int size) { int* p=&a; for(int i=0; isize; ++i) { p[i]=1; } } int main() { int Arr[]={1,2,3,4,5,6}; foo(*Arr, sizeof(Arr)/sizeof(Arr[0])); for(int i=0; isizeof(Arr)/sizeof(Arr[0]); ++i) std::cout[i]<'\n'; return 0; }

Регистрация: 27.09.2009
Сообщений: 101
Спасибо за помощь.

Эксперт С++

5827 / 3478 / 358
Регистрация: 08.02.2010
Сообщений: 7,448

Lavroff, у тебя ведь передача не массива, а первого элемента массива по ссылке
Vivo, а зачем Вам нужно передавать массив по ссылке?

Эксперт С++

8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562

Nameless One, Ну да. Ссылку на массив как-то проблемно организовать. Ссылка вроде как может быть только на элемент. Не?
Там вроде можно через указатель на ссылку или ссылку на указатель. Но утром думать лень

Регистрация: 27.09.2009
Сообщений: 101

Nameless One в задании по ООП сказано сделать:
4) Описать функцию сортировки массива по неубыванию любым доступным способом (для обмена использовать функцию в зависимости от выбора пользователя: встраиваемую функцию с аргументами – указателями на меняемые ячейки или встраиваемую функцию с аргументами – ссылками на меняемые ячейки. Функции должны называться одинаково, отличаться лишь аргументами).

Я тоже не пойму зачем делать передачу по ссылке. Так вариант предложенный Lavroff не правильный?

Эксперт С++

5827 / 3478 / 358
Регистрация: 08.02.2010
Сообщений: 7,448

Lavroff, Vivo, массив в С/С++ - это по сути указатель. Если нам необходимо изменить внутри функции содержимое массива (т.е. области памяти, на который он указывает), то нам достаточно передать его по указателю (пример - копирование строк (массивов символов) с помощью функции strcpy). Но если нам необходимо изменить значение самого указателя, то нам нужно передавать этот указатель по ссылке или опять-таки по указателю, так что предположение Lavroff'а верное (про ссылку на указатель). Вот пример - обмен двух значениями двух массивов:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include templateclass T> void swapArray(T*& arr1, T*& arr2) { T* temp = arr1; arr1 = arr2; arr2 = temp; } int main() { size_t size1 = 5; size_t size2 = 3; int* arr1 = new int[size1]; int* arr2 = new int[size2]; std::cout  <"---Before swapping---"  ::endl; std::cout  <"First array:"  ::endl; for(size_t i = 0; i  size1; ++i) { arr1[i] = i + 1; std::cout  [i]  ::endl; } std::cout  <"Second array:"  ::endl; for(size_t i = 0; i  size2; ++i) { arr2[i] = (i + 1) * 2; std::cout  [i]  ::endl; } swapArray(arr1, arr2); std::cout  ::endl; std::cout  <"---After swapping---"  ::endl; std::cout  <"First array:"  ::endl; for(size_t i = 0; i  size2; ++i) // Не забываем, что изменился размер массива arr1 std::cout  [i]  ::endl; std::cout  <"Second array:"  ::endl; for(size_t i = 0; i  size1; ++i) // Не забываем, что изменился размер массива arr2 std::cout  [i]  ::endl; delete[] arr1; delete[] arr2; return 0; }

По сути, здесь не происходит копирование элементов одного массива в другой - просто меняется значение двух указателей.

Nameless One в задании по ООП сказано сделать:
4) Описать функцию сортировки массива по неубыванию любым доступным способом (для обмена использовать функцию в зависимости от выбора пользователя: встраиваемую функцию с аргументами – указателями на меняемые ячейки или встраиваемую функцию с аргументами – ссылками на меняемые ячейки. Функции должны называться одинаково, отличаться лишь аргументами).

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
templateclass T> void swap(T* a, T* b) { T temp = *a; *a = *b; *b = temp; } //. пример использования: int a = 5, b = 6; swap(&a, &b); //. templateclass T> void swap(T& a, T& b) { T temp = a; a = b; b = temp; } //. пример использования int a = 5, b = 6; swap(a, b);

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

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

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