Передача массива в функцию и возврат из функции
Особенность передачи массивов в функции в языке Си в том, что передается не сам массив, а адрес массива, который хранится в локальном указателе на него.
Это можно сделать по-разному, но результат будет одинаковый:
- 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);
>
Как видите, программа передает массив в функцию по имени, а функция присваивает массиву элементы.
Таким образом, функция может изменить элементы массива, если ей это нужно.
Указатели как параметры. Передача массивов в функции
Это занятие начнем с рассмотрения указателей в качестве параметров функции.
До этого момента мы с вами объявляли и использовали указатели в рамках одной функции main() для изменения значений переменных. Например, так:
#include int main(void) { short var_a = 10; short* ptr = &var_a; *ptr = 5; return 0; }
Однако особого практического смысла от такой операции не было. Но теперь, когда мы имеем дело с функциями, указатели становятся одним из ключевых элементов. Давайте представим, что нам нужна функция, которая бы меняла значения между двумя переменными. Без указателей решение этой задачи было бы очень корявое. А вот с ними получаем вполне красивое решение:
void swap_2(int* a, int* b) { int t = *a; *a = *b; *b = t; } int main(void) { int x = 5, y = 10; swap_2(&x, &y); return 0; }
Как это работает? Вначале в функции main() выполняется вызов функции swap_2() с передачей адресов переменных x и y. В результате, указатель *a ссылается на переменную x, а указатель *b – на переменную y. Затем, в самой функции swap_2() происходит обмен значениями между указанными областями памяти, и, как следствие, меняются значения переменных x и y.
Почему это решение можно считать красивым? Во-первых, такая функция может менять значения между любыми целочисленными переменными типа int, где бы эти переменные ни были бы определены. Например, в приведенной программе, переменные x и y доступны только в пределах функции main() и недоступны (по их именам) за пределами этой функции. Поэтому напрямую получить к ним доступ в функции swap_2() не получится. Но через указатели это вполне можно сделать. Во-вторых, использование указателей позволяет нам определять функции, которые могут менять и возвращать более одного значения. Как вы помните, после оператора return можно указывать только одно значение (выражение), которое будет возвращено функцией. Прописывать несколько нельзя. Поэтому с возвратом множества значений возникают определенные трудности, которые, хотя, можно преодолеть, например, с помощью структур, о которых мы с вами еще будем говорить. Однако через указатели решить задачу с возвратом множества значений все же куда проще.
Передача массивов через параметры функции
Помимо изменения значений, указатели в параметрах используют с целью передачи массивов функциям. Как я ранее уже отмечал, в языке Си нет специального типа (структуры данных) для массивов. Их следует воспринимать, как непрерывную область памяти, в которой последовательно друг за другом расположены элементы массива:
short ar[5];
При этом имя массива ar можно воспринимать, как указатель на начало этой области. А раз так, то любой одномерный массив можно передать в функцию, используя указатели. Например, следующим образом:
#include int sum_ar(const short* arr, int length) { int res = 0; for(int i = 0; i length; ++i) res += arr[i]; return res; } int main(void) { short ar[] = {1, 2, 3, 4, 5}; int res = sum_ar(ar, sizeof(ar) / sizeof(*ar)); printf("res = %d\n", res); return 0; }
При вызове функции sum_ar() параметр arr будет ссылаться на первый элемент массива ar, а второй параметр length содержать число элементов массива. Затем, используя операцию квадратные скобки, мы через указатель arr перебираем элементы массива ar и подсчитываем их сумму, которая возвращается функцией sum_ar(). Ключевое слово const говорит о том, что значения массива внутри функции меняться не будут. Так принято делать, если данные по указателям только читаются и не меняются. Своего рода, правило хорошего тона при объявлении параметров-указателей.
Как видите, передать массив в функцию достаточно просто. Но здесь у вас может возникнуть вопрос, а зачем определять второй параметр length? Разве нельзя вычислить размер массива непосредственно в функции sum_ar()? Увы, нет. Указатель arr – это уже обычный указатель на ячейки памяти, а не на массив, поэтому операция sizeof() для него вернет размер указателя, а не области памяти, который занимает массив. Определить число элементов массива мы можем только, используя имя массива ar, но не указатель на него. Об этом ранее мы с вами уже говорили.
Если мы еще раз посмотрим на объявление функции sum_ar(), то первый параметр arr можно интерпретировать и как указатель на массив и как указатель на переменную типа short. Так вот, чтобы подчеркнуть, что arr ссылается именно на массив, допустимо этот указатель описывать следующим образом:
int sum_ar(const short arr[], int length) {. }
На уровне машинных кодов оба варианта абсолютно равнозначны. Это все тот же указатель arr, только синтаксис подсказывает программисту, что через ar в функции sum_ar() предполагается работать с непрерывной областью памяти, как с массивом. Не более того.
Также, при необходимости, функция может возвращать константные значения. Для этого достаточно в типе прописать ключевое слово const следующим образом:
const char* find_space(const char* buf) { while(*buf != '\0') { if(*buf == ' ') return buf; buf++; } }
Обычно const прописывают при возвращении константного указателя, так как в других ситуациях особого смысла в const нет. В данном примере, функция находит первый пробел в переданной строке и возвращает указатель на него. Если пробел не был найден, то возвращается значение NULL.
Передача многомерных массивов через параметры функции
Крайне редко, но все же бывают ситуации, когда нам нужно передать не одномерный, а многомерный массив в функцию. Для этого, как вы уже догадались, нужно использовать специальный тип указателя на соответствующий многомерный массив. Например, пусть у нас объявлен двумерный массив с некоторым числом строк и тремя столбцами:
short ar[][3] = {{1, 2, 3}, {4, 5, 6}};
Чтобы его передать через параметр в функцию, следует сделать следующее объявление:
void show_ar2D(const short (*ar)[3], int rows) { for(int i = 0;i rows; ++i) { for(int j = 0;j 3; ++j) printf("%d ", ar[i][j]); printf("\n"); } }
Эта функция принимает указатель на двумерный массив с произвольным числом строк rows, но обязательно тремя столбцами. И, затем, отображает его на экране в виде таблицы.
Использовать такую функцию можно следующим образом:
int main(void) { short ar[][3] = {{1, 2, 3}, {4, 5, 6}}; show_ar2D(ar, sizeof(ar) / sizeof(*ar)); return 0; }
Мы по-прежнему передаем указатель на массив и число строк вторым аргументом. После запуска программы увидим результат:
Как вы понимаете, недостатком использования и передачи многомерных массивов в функцию является необходимость указания точных размеров, кроме первого. В нашем случае, при объявлении функции, необходимо указывать число столбцов, равное трем. И если это значение изменится, то придется вносить правки во все места, где оно используется. Из этой ситуации есть, по крайней мере, два выхода. Первый очевидный. С помощью директивы define определить число столбцов и использовать макроимя в тексте программы:
#include #define COLS 3 void show_ar2D(const short (*ar)[COLS], int rows) { for(int i = 0;i rows; ++i) { for(int j = 0;j COLS; ++j) printf("%d ", ar[i][j]); printf("\n"); } } int main(void) { short ar[][COLS] = {{1, 2, 3}, {4, 5, 6}}; show_ar2D(ar, sizeof(ar) / sizeof(*ar)); return 0; }
Во втором случае определить одномерный массив и работать с ним, как с многомерным. Например, для получения аналога двумерного массива в COLS столбцов, можно использовать следующую формулу:
short ar[] = {1, 2, 3, 4, 5, 6}; short res_ij = ar[i * COLS + j]; // i – индекс строк; j – индекс столбцов
Какой именно вариант использовать на практике зависит от конкретной решаемой задачи и предпочтений программиста.
Видео по теме
Язык Си. Рождение легенды
#1. Этапы трансляции программы в машинный код. Стандарты
#2. Установка компилятора gcc и Visual Studio Code на ОС Windows
#3. Структура и понимание работы программы «Hello, World!»
#4. Двоичная, шестнадцатеричная и восьмеричная системы счисления
#5. Переменные и их базовые типы. Модификаторы unsigned и signed
#6. Операция присваивания. Числовые и символьные литералы. Операция sizeof
#7. Стандартные потоки ввода/вывода. Функции putchar() и getchar()
#8. Функция printf() для форматированного вывода
#9. Функция scanf() для форматированного ввода
#10. Арифметические операции: сложение, вычитание, умножение и деление
#11. Арифметические операции деления по модулю, инкремента и декремента
#12. Арифметические операции +=, -=, *=, /=, %=
#13. Булевый тип. Операции сравнения. Логические И, ИЛИ, НЕ
#14. Условный оператор if. Конструкция if-else
#15. Условное тернарное выражение
#16. Оператор switch множественного выбора. Ключевое слово break
#17. Битовые операции И, ИЛИ, НЕ, XOR. Сдвиговые операции
#18. Генерация псевдослучайных чисел. Функции математической библиотеки
#19. Директивы макропроцессора #define и #undef
#20. Директива #define для определения макросов-функций. Операции # и ##
#21. Директивы #include и условной компиляции
#22. Оператор цикла while
#23. Оператор цикла for
#24. Цикл do-while с постусловием. Вложенные циклы
#25. Операторы break, continue и goto
#26. Указатели. Проще простого
#27. Указатели. Приведение типов. Константа NULL
#28. Долгожданная адресная арифметика
#29. Введение в массивы
#30. Вычисление размера массива. Инициализация массивов
#31. Указатели на массивы
#32. Ключевое слово const с указателями и переменными
#33. Операции с массивами копирование, вставка, удаление и сортировка
#34. Двумерные и многомерные массивы. Указатели на двумерные массивы
#35. Строки. Способы объявления, escape-последовательности
#36. Ввод/вывод строк в стандартные потоки
#37. Строковые функции strlen(), strcpy(), strncpy(), strcat(), strncat()
#38. Строковые функции сравнения, поиска символов и фрагментов
#39. Строковые функции sprintf(), atoi(), atol(), atoll() и atof()
#40. Объявление и вызов функций
#41. Оператор return. Вызов функций в аргументах
#42. Прототипы функций
#43. Указатели как параметры. Передача массивов в функции
#44. Указатели на функцию. Функция как параметр (callback)
#45. Стековый фрейм. Автоматические переменные
#46. Рекурсивные функции
#47. Функции с произвольным числом параметров
#48. Локальные и глобальные переменные
#49. Локальные во вложенных блоках
#50. Ключевые слова static и extern
#51. Функции malloc(), free(), calloc(), realloc(), memcpy() и memmove()
#52. Перечисления (enum). Директива typedef
#53. Структуры. Вложенные структуры
#54. Указатели на структуры. Передача структур в функции
#55. Реализация стека (пример использования структур)
#56. Объединения (union). Битовые поля
#57. Файловые функции: fopen(), fclose(), fgetc(), fputc()
#58. Функции perror(), fseek() и ftell()
#59. Функции fputs(), fgets() и fprintf(), fscanf()
#60. Функции feof(), fflush(), setvbuf()
#61. Бинарный режим доступа. Функции fwrite() и fread()
#1. Первая программа на С++
#2. Ввод-вывод с помощью объектов cin и cout
#3. Пространства имен (namespace)
#4. Оператор using
#5. Новые типы данных. Приведение типов указателей
#6. Инициализация переменных. Ключевые слова auto и decltype
#7. Ссылки. Константные ссылки
#8. Объект-строка string. Операции с объектами класса string
#9. Файловые потоки. Открытие и закрытие файлов. Режимы доступа
#10. Чтение и запись данных в файл в текстовом режиме
#11. Чтение и запись данных в файл в бинарном режиме
#12. Перегрузка функций. Директива extern C
#13. Значения параметров функции по умолчанию
#15. Лямбда-выражения. Объявление и вызов
#16. Захват внешних значений в лямбда выражениях
#17. Структуры в С++, как обновленный тип данных
#18. Структуры. Режимы доступа. Сеттеры и геттеры
#19. Структуры. Конструкторы и деструкторы
#20. Операторы new / delete и new [] / delete []
© 2023 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Как передать массив в функцию си
Ваши программы будут передавать массивы в функции точно так же, как и любые другие переменные. Функция может инициализировать массив, прибавить к массиву значения или вывести элементы массива на экран. Когда вы передаете массив в функцию, вы должны указать тип массива. Нет необходимости указывать размер массива. Вместо этого вы передаете параметр например number_of_elements, который содержит количество элементов в массиве:
void some_function(int array[], int number_of_elements);
Следующая программа ARRAYFUN.CPP передает массивы в функцию show_array, которая использует цикл for для вывода значений массивов:
#include void show_array (int array [] , int number_of_elements) < int i; for (i = 0; i < number_of_elements; i++) cout void main(void) < int little_numbers[5] =; int big_numbers[3] = < 1000, 2000, 3000 >; show_array(little_numbers, 5); show_array(big_numbers, 3); >
Как видите, программа просто передает массив в функцию по имени, а также указывает параметр, который сообщает функции количество элементов, содержащихся в массиве:
show_array(little_numbers, 5);
Следующая программа GETARRAY.CPP использует функцию get_values, ч тобы присвоить три значения массиву numbers:
#include void get_values(int array[], int number_of_elements) < int i; for (i = 0; i < number_of_elements; i++) < cout> array [i]; > > void main(void)
Как видите, программа передает массив в функцию по имени. Функция в свою очередь присваивает массиву элементы. Из урока 10 вы узнали, что, пока ваша программа не передаст параметры в функцию с помощью адреса, функция не может изменить эти параметры. Однако, как можно заметить в данном случае, функция get_values изменяет параметр-массив numbers. Как вы узнаете из урока 20, C++ действительно передает массивы в функцию, используя указатели. Таким образом, функция может изменить элементы массива, если ей это нужно.
Последний раз редактировалось MaTBeu; 08.02.2009 в 14:58 .
Как передать массив в функцию?
Если размер массива неизвестен, то можно использовать шаблон:
template void f(int (&arr)[N]) <>
Если то что передается в функцию — это не массив, а указатель (т.е. не T[N] a T* ), то функция должна принимать указатель, а размер надо передавать дополнительным параметром или как-то еще.
void f(int* ptr, std::size_t n) <>
Однако это очень ненадежное решение, нет никакой гарантии что будет передан правильный размер массива, или что ptr не равен nullptr .
По этому правильно — это использовать span из C++ Core Guidelines
void f(gsl::span arr) <>
Также, вместо массива T[N] можно передавать ссылку на std::array
void f(std::array& v) <>
А вместо динамического массива — использовать std::vector :
void f(std::vector& v) <> -- или -- void f(const std::vector& v) <>