Почему C ++ не поддерживает функции, возвращающие массивы?

Некоторые языки позволяют просто объявить функцию, возвращающую массив как обычную функцию, например Java:

public String[] funcarray() { String[] test = new String[]{"hi", "hello"}; return test; } 

Почему C ++ не поддерживает что-то вроде int[] funcarray(){} ? Вы можете вернуть массив, но это настоящая хлопот, чтобы сделать такую ​​функцию. Кроме того, я слышал, что строки – это просто массивы символов. Итак, если вы можете вернуть строку на C ++, почему бы не массив?

9 Solutions collect form web for “Почему C ++ не поддерживает функции, возвращающие массивы?”

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

Давайте сначала подумаем о C. На языке C существует четкое различие между «pass by reference» и «pass by value». Чтобы относиться к нему легко, имя массива в C действительно является указателем. Во всех смыслах и целях разница (обычно) сводится к распределению. Код

 int array[n]; 

создаст 4 * n байт памяти (в 32-разрядной системе) в стеке, соответствующий масштабу того, какой блок кода делает объявление. В очереди,

 int* array = (int*) malloc(sizeof(int)*n); 

будет создавать память того же объема, но в куче. В этом случае то, что в этой памяти не привязано к области видимости, ограничивается только ссылкой на память. Здесь передается значение по значению и передается по ссылке. Передача по значению, как вы, вероятно, знаете, означает, что когда что-то передается или возвращается из функции, передаваемая «вещь» является результатом оценки переменной. Другими словами,

 int n = 4; printf("%d", n); 

будет печатать число 4, потому что конструкция n оценивается до 4 (извините, если это элементарно, я просто хочу охватить все базы). Это 4 абсолютно не имеет отношения к пространству памяти вашей программы, это просто буквальное, и поэтому, когда вы покидаете область действия, в которой этот контекст 4, вы его теряете. Как насчет прохождения по ссылке? Передача по ссылке ничем не отличается в контексте функции; вы просто оцениваете переданную конструкцию. Единственное отличие состоит в том, что после оценки пройденной «вещи» вы используете результат оценки в качестве адреса памяти. У меня когда-то был особый циничный преподаватель CS, который любил заявлять, что нет такой вещи, как передача по ссылке, просто способ передать умные ценности. Действительно, он прав. Итак, теперь мы рассматриваем сферу действия в терминах функции. Притворись, что у вас может быть тип возвращаемого массива:

 int[] foo(args){ result[n]; // Some code return result; } 

Проблема здесь в том, что результат оценивается по адресу 0-го элемента массива. Но когда вы пытаетесь получить доступ к этой памяти из-за пределов этой функции (через возвращаемое значение), у вас возникла проблема, потому что вы пытаетесь получить доступ к памяти, которая не входит в область действия, с которой вы работаете (стек вызова функции). Таким образом, мы обходим это со стандартным «переходом по ссылке» jiggery-pokery:

 int* foo(args){ int* result = (int*) malloc(sizeof(int)*n)); // Some code return result; } 

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

В чем мой смысл? В Java принято утверждать, что «все передается по значению». Это правда. Тот же циничный инструктор сверху также имел это, чтобы сказать о Java и ООП в целом: все просто указатель. И он тоже прав. Хотя все в Java фактически передается по значению, почти все эти значения являются фактически адресами памяти. Таким образом, в Java язык позволяет вам возвращать массив или строку, но он делает это, превращая его в версию с указателями для вас. Он также управляет вашей памятью для вас. А автоматическое управление памятью, хотя и полезно, неэффективно.

Это приводит нас к C ++. Вся причина, по которой C ++ была изобретена, заключалась в том, что Бьярн Страуструп экспериментировал с Simula (в основном оригинальным OOPL) во время его работы в PhD, и считал, что это было фантастически концептуально, но он заметил, что он выполнялся довольно ужасно. И поэтому он начал работать над тем, что называлось C с classами, которое переименовано в C ++. При этом его цель заключалась в том, чтобы сделать язык программирования, который принял НЕКОТОРЫЕ из лучших функций от Simula, но оставался сильным и быстрым. Он решил расширить C благодаря своей уже легендарной производительности, и одним из компромиссов было то, что он решил не реализовывать автоматическое управление памятью или garbage collection в таких больших масштабах, как другие OOPL. Возrotation массива из одного из classов шаблонов работает, потому что вы используете class. Но если вы хотите вернуть массив C, вы должны сделать это способом C. Другими словами, C ++ поддерживает возврат массива ТОЧНО так же, как Java; он просто не выполняет всю работу за вас. Потому что датский чувак подумал, что это будет слишком медленно.

C ++ поддерживает это – хорошо вроде:

 vector< string> func() { vector res; res.push_back( "hello" ); res.push_back( "world" ); return res; } 

Даже C-тип поддерживает его:

 struct somearray { struct somestruct d[50]; }; struct somearray func() { struct somearray res; for( int i = 0; i < 50; ++i ) { res.d[i] = whatever; } // fill them all in return res; } 

std::string - это class, но когда вы говорите строку, вы, вероятно, имеете в виду литерал. Вы можете безопасно возвращать литерал из функции, но на самом деле вы можете статически создавать любой массив и возвращать его из функции. Это было бы поточно-безопасным, если бы это был массив const (только для чтения), который имеет место со строковыми литералами.

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

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

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

Они дали обходное решение о включении массива в структуру, и именно поэтому он должен оставаться на C ++, потому что он использует одно и то же соглашение о вызовах.

Примечание. В таких языках, как Java, массив является classом. Вы создаете одно с новым. Вы можете переназначить их (это l-значения).

Массивы в C (и на C ++ для обратной совместимости) имеют специальную семантику, которая отличается от остальных типов. В частности, в то время как для остальных типов C имеет только семантику pass-by-value, в случае массивов эффект синтаксиса pass-by-value имитирует pass-by-reference странным образом:

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

Из-за этого конкретного обращения к массивам – они не могут быть переданы по значению – они также не могут быть возвращены по значению. В C вы можете вернуть указатель, а на C ++ вы также можете вернуть ссылку, но сам массив не может быть выделен в стеке.

Если вы думаете об этом, это не отличается от языка, который вы используете в вопросе, так как массив динамически распределяется, и вы возвращаете только указатель / ссылку на него.

С другой стороны, язык C ++ допускает различные решения этой конкретной проблемы, такие как использование std::vector в текущем стандарте (содержимое динамически распределено) или std::array в предстоящем стандарте (содержимое может быть выделено в стеке , но это может иметь большую стоимость, так как каждый элемент должен быть скопирован в тех случаях, когда копия не может быть удалена компилятором). Фактически, вы можете использовать тот же тип подхода с текущим стандартом, используя готовые библиотеки, такие как boost::array .

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

Из обсуждения здесь:

http://forum.codecall.net/cc/32457-function-return-array-c.html

Другие сказали, что в C ++ один использует вектор <> вместо массивов, унаследованных от C.

Итак, почему C ++ не позволяет возвращать массивы C? Потому что C нет.

Почему C нет? Поскольку C эволюционировал из B, нетипизированный язык, в котором возrotation массива вообще не имеет смысла. При добавлении типов в B было бы целесообразно вернуть массив, но это не было сделано для того, чтобы сохранить некоторые идиомы B действительными и облегчить преобразование программ из B в C. И с тех пор возможность сделать C-массивы более полезными, как всегда было отказано (и даже больше, даже не рассмотрено), поскольку это сломало бы слишком много существующего кода.

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

 public std::string* funcarray() { std::string* test = new std::string[2]; test[0] = "hi"; test[1] = "hello"; return test; } // somewhere else: std::string* arr = funcarray(); std::cout < < arr[0] << " MisterSir" << std::endl; delete[] arr; 

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

«Почему C ++ не поддерживает что-то вроде»: потому что это не имеет никакого смысла. В языках на основе ссылок, таких как JAVA или PHP, управление памятью основано на сборке мусора. Части памяти, которые не имеют ссылок (никакая переменная в вашей программе не указывает на нее больше), автоматически освобождаются. В этом контексте вы можете выделить память и осторожно передать ссылку.

Код C ++ будет переведен на машинный код, и в нем нет GC. Таким образом, в C и C ++ существует сильное чувство владения блоками памяти. Вы должны знать, будет ли указатель, который вы отправляете, бесплатно для вас в любое время (на самом деле вы освободите его после использования), или указатель на общую часть памяти, которая является абсолютной no-no для бесплатной.

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

Будет ли массив, возвращаемый функцией, всегда быть копией (ваш бесплатно), или вы должны сделать их копии? Вы выиграли бы Whet, получив массив с указателем на массив?

Верните std::vector<> вместо массива. В общем случае массивы не работают с C ++, и их обычно следует избегать.

Кроме того, string тип данных – это не просто массив символов, хотя «строка с кавычками». string управляет массивом символов, и вы можете получить к ней доступ с помощью .c_str() , но для этой string больше.

Проверьте здесь. Действительно полезно.

  • Как вернуть массив из функции?
  • C ++ Возвращаемый массив многомерности из функции
  • Возвращает 2d-массив из функции в C ++
  • Разница между . и: в Луа
  • Как изменить реализацию (обход) функции, объявленной извне
  • Поддерживает ли Ninject Func (автоматически сгенерированный завод)?
  • объекты data.table, назначенные с помощью: = из функции, не напечатанной
  • Как я могу создать функцию с обработчиком завершения в Swift?
  • Когда функция слишком длинная?
  • Вызвать функцию, названную в строковой переменной в C
  • jquery - не является функцией ошибки
  • C вызовы функции: Понимание правила «неявного int»
  • Определить имена таблиц и столбцов в качестве аргументов в функции plpgsql?
  • Выделить память 2d массива в функции C
  • Давайте будем гением компьютера.