По-видимому, мы можем передавать сложные экземпляры классов в функции, но почему мы не можем передавать массивы в функции?
Почему мы не можем передавать массивы по значению?
Ответ 1
Происхождение является историческим. Проблема в том, что правило "распад массивов в указатели при передаче функции" прост.
Копирование массивов будет довольно сложным и не очень ясным, так как поведение изменится для разных параметров и различных деклараций функций.
Обратите внимание, что вы все равно можете сделать косвенный проход по значению:
struct A { int arr[2]; };
void func(struct A);
Ответ 2
Здесь другая перспектива: в C. нет массива "массив". Скорее, T[N]
- это другой тип для каждого N
. Итак, T[1]
, T[2]
и т.д. - все разные типы.
В C нет перегрузки функций, и поэтому единственной разумной вещью, которую вы могли бы разрешить, была бы функция, которая принимает (или возвращает) один тип массива:
void foo(int a[3]); // hypothetical
Предположительно, это считалось гораздо менее полезным, чем фактическое решение о том, чтобы все массивы распадались на указатель на первый элемент и требовали от пользователя передавать размер другим способом. В конце концов, вышеупомянутое может быть переписано как:
void foo(int * a)
{
static const unsigned int N = 3;
/* ... */
}
Таким образом, нет никакой потери выразительной силы, но огромный выигрыш в общности.
Обратите внимание, что это не отличается от С++, но генерация кода с помощью шаблонов позволяет вам написать шаблонную функцию foo(T (&a)[N])
, где N
выводится для вас, но это просто означает, что вы можете создать целое семейство различных, разных функций, по одному для каждого значения N
.
В качестве крайнего случая представьте, что вам понадобится две функции print6(const char[6])
и print12(const char[12])
, чтобы сказать print6("Hello")
и print12("Hello World")
, если вы не захотите разлагать массивы на указатели, d необходимо добавить явное преобразование, print_p((const char*)"Hello World")
.
Ответ 3
Отвечая на очень старый вопрос, поскольку Вопрос является рынком с С++, просто добавляющим для целей завершения, мы можем использовать std:: array и передать массивы функциям по значению или по ссылке, которая дает защиту от доступа к связанным индексам:
ниже - образец:
#include <iostream>
#include <array>
//pass array by reference
template<size_t N>
void fill_array(std::array<int, N>& arr){
for(int idx = 0; idx < arr.size(); ++idx)
arr[idx] = idx*idx;
}
//pass array by value
template<size_t N>
void print_array(std::array<int, N> arr){
for(int idx = 0; idx < arr.size(); ++idx)
std::cout << arr[idx] << std::endl;
}
int main()
{
std::array<int, 5> arr;
fill_array(arr);
print_array(arr);
//use different size
std::array<int, 10> arr2;
fill_array(arr2);
print_array(arr2);
}
Ответ 4
Причина, по которой вы не можете передать массив по значению, заключается в том, что нет конкретного способа отслеживать размер массива, чтобы логика вызова функции знала, сколько памяти выделяется и что копировать. Вы можете передать экземпляр класса, поскольку классы имеют конструкторы. Массивы не делают.
Ответ 5
Эквивалент этого заключается в том, чтобы сначала сделать копию массива, а затем передать его функции (которая может быть очень неэффективной для больших массивов).
Кроме этого, я бы сказал это по историческим причинам, т.е. нельзя было передавать массивы по значению в C.
Моя догадка заключается в том, что аргументация, лежащая в основе NOT представления передаваемых массивов по значению в С++, заключалась в том, что объекты считались умеренными по размеру по сравнению с массивами.
Как указано delnan, при использовании std::vector
вы можете передавать объекты, похожие на массив, в функции по значению.
Ответ 6
Вы передаете по значению: значение указателя на массив. Помните, что использование обозначения квадратной скобки в C является просто сокращением для удаления ссылки на указатель. ptr [2] означает * (ptr + 2).
Отбрасывание скобок дает вам указатель на массив, который может передаваться по значению функции:
int x[2] = {1, 2};
int result;
result = DoSomething(x);
См. список типов в спецификации ANSI C. Массивы не являются примитивными типами, но построены из комбинации указателей и операторов. (Это не позволит мне добавить другую ссылку, но конструкция описана в разделе "Вывод типа массива".)
Ответ 7
Summery:
- Передача адреса первого элемента массива
&a = a = &(a[0])
- Новый указатель (новый указатель, новый адрес, 4 байта в памяти)
- Указывает на то же расположение памяти, в различном типе.
Пример 1:
void by_value(bool* arr) // pointer_value passed by value
{
arr[1] = true;
arr = NULL; // temporary pointer that points to original array
}
int main()
{
bool a[3] = {};
cout << a[1] << endl; // 0
by_value(a);
cout << a[1] << endl; // 1 !!!
}
Адрес:
[main]
a = 0046FB18 // **Original**
&a = 0046FB18 // **Original**
[func]
arr = 0046FB18 // **Original**
&arr = 0046FA44 // TempPTR
[func]
arr = NULL
&arr = 0046FA44 // TempPTR
Пример 2:
void by_value(bool* arr)
{
cout << &arr << arr; // &arr != arr
}
int main()
{
bool a[3] = {};
cout << &a << a; // &a == a == &a[0]
by_value(arr);
}
Адреса
Prints:
[main] 0046FB18 = 0046FB18
[func] 0046FA44 != 0046FB18
Обратите внимание:
- & (required-lvalue): lvalue -to- > rvalue
- Array Decay: новый указатель (временный) указывает на адрес массива (по значению)
ЧИТАТЬ:
Ответ 8
Это было сделано таким образом, чтобы сохранить синтаксическую и семантическую совместимость с языком B, в котором массивы были реализованы как физические указатели.
Прямой ответ на этот вопрос дан в Деннисе Ритчи "Развитие языка Си", см. Раздел "Критика". Это говорит
Например, пустые квадратные скобки в объявлении функции
int f(a) int a[]; { ... }
являются живым ископаемым, остатком NB способа объявления указателя;
a
, только в этом особом случае, интерпретируется в C как указатель. Обозначения сохранились частично ради совместимости, в частности в рамках рационализации, что позволило бы программистам сообщить свои читателям о намерении передатьf
указатель генерируемого из массива, а не ссылка на одного целое. К сожалению, это так же смущает ученика, как и предупреждает читателя.
Это должно быть принято в контексте предыдущей части статьи, особенно "Embryonic C", которая объясняет, как введение struct
типов в C привело к отказу от подхода B- и стиля BCPL для реализации массивов (то есть как обычных указателей).). C переключился на реализацию массива без указателей, сохранив эту унаследованную семантику стиля B- только в списках параметров функции.
Таким образом, текущий вариант поведения параметров массива является результатом компромисса: с одной стороны, мы должны были иметь копируемые массивы в struct
s, с другой стороны, мы хотели сохранить семантическую совместимость с функциями, написанными на B, где массивы всегда передаются "по указателю".
Ответ 9
на самом деле, указатель на массив передается значением, использование этого указателя внутри вызываемой функции даст вам ощущение, что массив передается по ссылке, что неверно. попробуйте изменить значение в указателе массива, чтобы указать на другой массив в вашей функции, и вы обнаружите, что исходный массив не был затронут, что означает, что массив не передается по ссылке.