Подтвердить что ты не робот

Почему мы не можем передавать массивы по значению?

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

4b9b3361

Ответ 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: новый указатель (временный) указывает на адрес массива (по значению)

ЧИТАТЬ:

Rvalue

Массив распада

Ответ 8

Это было сделано таким образом, чтобы сохранить синтаксическую и семантическую совместимость с языком B, в котором массивы были реализованы как физические указатели.

Прямой ответ на этот вопрос дан в Деннисе Ритчи "Развитие языка Си", см. Раздел "Критика". Это говорит

Например, пустые квадратные скобки в объявлении функции

int f(a) int a[]; { ... }

являются живым ископаемым, остатком NB способа объявления указателя; a, только в этом особом случае, интерпретируется в C как указатель. Обозначения сохранились частично ради совместимости, в частности в рамках рационализации, что позволило бы программистам сообщить свои читателям о намерении передать f указатель генерируемого из массива, а не ссылка на одного целое. К сожалению, это так же смущает ученика, как и предупреждает читателя.

Это должно быть принято в контексте предыдущей части статьи, особенно "Embryonic C", которая объясняет, как введение struct типов в C привело к отказу от подхода B- и стиля BCPL для реализации массивов (то есть как обычных указателей).). C переключился на реализацию массива без указателей, сохранив эту унаследованную семантику стиля B- только в списках параметров функции.

Таким образом, текущий вариант поведения параметров массива является результатом компромисса: с одной стороны, мы должны были иметь копируемые массивы в struct s, с другой стороны, мы хотели сохранить семантическую совместимость с функциями, написанными на B, где массивы всегда передаются "по указателю".

Ответ 9

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