Почему C или C++ не позволяют передавать массив по значениям в функцию

C и C++ позволяют передавать структуру и объекты по значению в функцию, хотя и препятствуют передаче массивов по значениям, почему?

7 ответов

В C/C++, внутренне, массив передается как указатель на какое-то место, и в основном он передается по значению. Дело в том, что скопированное значение представляет адрес памяти в том же месте.

В C++ vector<T> кстати, копируется и передается в другую функцию.

Вы можете передавать массив по значению, но вы должны сначала обернуть его в структуру или класс. Или просто используйте тип, такой как std::vector.

Я думаю, что решение было ради эффективности. Никто не хотел бы делать это большую часть времени. Это то же самое рассуждение, что и почему нет двойных без знака. Нет связанной инструкции CPU, поэтому вы должны сделать то, что неэффективно, очень сложно в таком языке, как C++.

Как упомянул @litb: "C++1x и boost оба обернули собственные массивы в структуры, обеспечивающие std::array и boost::array, что я всегда предпочел бы, потому что это позволяет передавать и возвращать массивы внутри структур"

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

Большинство людей считают, что вы должны передать массив в качестве указателя и указать размер в качестве отдельного параметра, но это не нужно. Вы можете передать ссылку на сам фактический массив, сохраняя его статус sizeof().

//Here you need the size because you have reduced 
// your array to an int* pointing to the first element.
void test1(int *x, int size)
{
  assert(sizeof(x) == 4);
}

//This function can take in an array of size 10
void test2(int (&x)[10])
{
  assert(sizeof(x) == 40);
}

//Same as test2 but by pointer
void test3(int (*x)[10])
{
  assert(sizeof(*x) == 40);
  //Note to access elements you need to do: (*x)[i]
}

Некоторые люди могут сказать, что размер массива неизвестен. Это неправда.

int x[10];  
assert(sizeof(x) == 40);

Но как насчет распределений в куче? Выделения в куче не возвращают массив. Они возвращают указатель на первый элемент массива. Таким образом, новый тип не является безопасным. Если у вас действительно есть переменная массива, то вы будете знать размер ее содержимого.

РЕДАКТИРОВАТЬ: я оставил оригинальный ответ ниже, но я считаю, что большая часть значения теперь в комментариях. Я сделал это вики-сообществом, поэтому, если кто-то из участников последующего разговора захочет отредактировать ответ, чтобы отразить эту информацию, не стесняйтесь.

Оригинальный ответ

Во-первых, как он узнает, какой объем стека выделить? Это исправлено для структур и объектов (я полагаю), но с массивом это будет зависеть от размера массива, который неизвестен до времени выполнения. (Даже если каждый вызывающий знает во время компиляции, могут быть разные вызывающие с разными размерами массива.) Вы можете указать конкретный размер массива в объявлении параметра, но это кажется немного странным.

Кроме того, как говорит Брайан, есть вопрос эффективности.

Чего бы вы хотели достичь через все это? Нужно ли убедиться, что содержимое исходного массива не изменилось?

Я думаю, что есть 3 основные причины, почему массивы передаются как указатели в C, а не по значению. Первые 2 упоминаются в других ответах:

  • эффективность
  • потому что вообще нет информации о размере для массивов (если вы включаете динамически размещаемые массивы)

Тем не менее, я думаю, что третья причина связана с:

  • эволюция C от более ранних языков, таких как B и BCPL, где массивы были фактически реализованы как указатель на данные массива

Деннис Ритчи говорит о ранней эволюции C от таких языков, как BCPL и B, и, в частности, о том, как реализованы массивы и как на них влияют массивы BCPL и B, и как и почему они различаются (оставаясь очень похожими в выражениях, поскольку имена массивов затухают в указатели в выражениях).

Это один из тех ответов "просто потому что". C++ унаследовал его от C, и должен был следовать ему, чтобы сохранить совместимость. Так было сделано в Си для эффективности. Вы редко захотите сделать копию большого массива (помните, подумайте здесь, PDP-11) в стеке, чтобы передать его функции.

На самом деле я не знаю ни одного языка, который бы поддерживал передачу голых массивов по значению. Это не было бы особенно полезно и могло бы быстро сжать стек вызовов.

Изменить: Для downvoters - если вы знаете лучше, пожалуйста, сообщите нам всем.

из C How To Program-Deitel p262

.. "C автоматически передает массивы функциям по ссылке. Имя массива оценивается как адрес первого элемента массива. Поскольку передается начальный адрес массива, вызываемая функция точно знает, где хранится массив. Поэтому, когда вызываемая функция изменяет элементы массива в теле своей функции, она изменяет фактические элементы массива в их исходных местах памяти».

это помогло мне, надеюсь, что это поможет и вам

Другие вопросы по тегам