Возвращаемый массив в функции

У меня есть массив int arr[5], который передается функции fillarr(int arr[]):

int fillarr(int arr[])
{
    for(...);
    return arr;
}
  • Как я могу вернуть этот массив?
  • Как я буду использовать его, скажем, я вернул указатель, как мне получить доступ к нему?

Ответ 1

В этом случае ваша переменная массива arr также может быть обработана как указатель на начало блока массива в памяти, путем неявного преобразования. Этот синтаксис, который вы используете:

int fillarr(int arr[])

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

int fillarr(int* arr)

Итак, в том же смысле, то, что вы хотите вернуть из своей функции, на самом деле является указателем на первый элемент массива:

int* fillarr(int arr[])

И вы все равно сможете использовать его так же, как обычный нормальный массив:

int main()
{
  int y[10];
  int *a = fillarr(y);
  cout << a[0] << endl;
}

Ответ 2

Функции С++ не могут возвращать массивы C-стиля по значению. Самое близкое - вернуть указатель. Кроме того, тип массива в списке аргументов просто преобразуется в указатель.

int *fillarr( int arr[] ) { // arr "decays" to type int *
    return arr;
}

Вы можете улучшить его, используя ссылки массива для аргумента и возврата, что предотвращает распад:

int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
    return arr;
}

С Boost или С++ 11, передача по ссылке является необязательной, а синтаксис меньше ума:

array< int, 5 > &fillarr( array< int, 5 > &arr ) {
    return arr; // "array" being boost::array or std::array
}

Шаблон array просто генерирует struct, содержащий массив C-style, поэтому вы можете применить объектно-ориентированную семантику, но сохраните первоначальную простоту массива.

Ответ 3

$8.3.5/8 состояний -

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

int (&fn1(int (&arr)[5]))[5]{     // declare fn1 as returning refernce to array
   return arr;
}

int *fn2(int arr[]){              // declare fn2 as returning pointer to array
   return arr;
}


int main(){
   int buf[5];
   fn1(buf);
   fn2(buf);
}

Ответ 4

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

std::vector<int> fillarr( std::vector<int> arr ) {
    // do something
    return arr;
}

Это сделает то, что вы ожидаете от этого. Поверхность заключается в том, что std::vector заботится о том, чтобы все было обработано чисто. недостатком является то, что это копирует очень большой объем данных, если ваш массив большой. Фактически он копирует каждый элемент массива дважды. сначала он копирует вектор, чтобы функция могла использовать его в качестве параметра. затем он копирует его снова, чтобы вернуть его вызывающему. Если вы можете справиться с управлением вектором самостоятельно, вы можете сделать что-то более легко. (он может скопировать его в третий раз, если вызывающему нужно сохранить его в какой-либо переменной, чтобы сделать больше вычислений)

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

void fillarr(std::vector<int> &  arr) {
    // modify arr
    // don't return anything
}

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

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

std::auto_ptr<std::vector<int> > fillarr( const std::vector<int> & arr) {
    std::auto_ptr<std::vector<int> > myArr(new std::vector<int>);
    // do stuff with arr and *myArr
    return myArr;
}

По большей части использование *myArr работает одинаково с использованием простого вектора ванили. Этот пример также изменяет список параметров, добавляя ключевое слово const. Теперь вы получаете ссылку, не копируя ее, но вы не можете ее изменить, поэтому вызывающий абонент знает, что она будет такой же, как до того, как функция добралась до нее.

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

template <class Iterator>
Iterator fillarr(Iterator arrStart, Iterator arrEnd) {
    Iterator arrIter = arrStart;
    for(;arrIter <= arrEnd; arrIter++)
       ;// do something
    return arrStart;
}

Использование этого выглядит немного странно, если вы не привыкли видеть этот стиль.

vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());

foo теперь "указывает на" начало модифицированного arr.

Что действительно приятно в том, что он одинаково хорошо работает на векторе, как на простых C-массивах и многих других типах коллекции, например

int arr[100];
int *foo = fillarr(arr, arr+100);

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

Ответ 5

В С++ 11 вы можете вернуть std::array.

#include <array>
using namespace std;

array<int, 5> fillarr(int arr[])
{
    array<int, 5> arr2;
    for(int i=0; i<5; ++i) {
        arr2[i]=arr[i]*2;
    }
    return arr2;
}

Ответ 6

Это:

int fillarr(int arr[])

фактически обрабатывается так же, как:

int fillarr(int *arr)

Теперь, если вы действительно хотите вернуть массив, вы можете изменить эту строку на

int * fillarr(int arr[]){
    // do something to arr
    return arr;
}

Это не возвращает массив. вы возвращаете указатель на начало адрес массива.

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

например.

int fillarr(int arr[]){
   array[0] = 10;
   array[1] = 5;
}

int main(int argc, char* argv[]){
   int arr[] = { 1,2,3,4,5 };

   // arr[0] == 1
   // arr[1] == 2 etc
   int result = fillarr(arr);
   // arr[0] == 10
   // arr[1] == 5    
   return 0;
}

Я предлагаю вам подумать о том, чтобы положить длину в вашу функцию fillarr, например это.

int * fillarr(int arr[], int length)

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

Чтобы правильно использовать его. Сделайте что-то вроде этого:

int * fillarr(int arr[], int length){
   for (int i = 0; i < length; ++i){
      // arr[i] = ? // do what you want to do here
   }
   return arr;
}

// then where you want to use it.
int arr[5];
int *arr2;

arr2 = fillarr(arr, 5);

// at this point, arr & arr2 are basically the same, just slightly
// different types.  You can cast arr to a (char*) and it'll be the same.

Если все, что вы хотите сделать, - установить массив на некоторые значения по умолчанию, подумайте об использовании встроенная функция memset.

что-то вроде:   memset ((int *) & arr, 5, sizeof (int));

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

Есть много учебников. Вот один из них, который дает вам представление о том, как их использовать. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html

Ответ 7

чтобы вернуть массив из функции, определим этот массив в структуре; Итак, это выглядит примерно так.

struct Marks{
   int list[5];
}

Теперь создадим переменные структуры типа.

typedef struct Marks marks;
marks marks_list;

Мы можем передать массив в функцию следующим образом и присвоить ему значение:

void setMarks(int marks_array[]){
   for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
       marks_list.list[i]=marks_array[i];
}

Мы также можем вернуть массив. Чтобы вернуть массив, возвращаемый тип функции должен быть типа структуры, т.е. меток. Это связано с тем, что в действительности мы передаем структуру, содержащую массив. Таким образом, окончательный код может выглядеть так.

marks getMarks(){
 return marks_list;
}

Ответ 8

Самый простой способ сделать это - вернуть его по ссылке, даже если вы не пишете '&' символ, он автоматически возвращается ссылкой

     void fillarr(int arr[5])
  {
       for(...);

  }

Ответ 9

int *fillarr(int arr[])

Вы все равно можете использовать результат, например

int *returned_array = fillarr(some_other_array);
if(returned_array[0] == 3)
    do_important_cool_stuff();

Ответ 10

template<typename T, size_t N>
using ARR_REF = T (&)[N];

template <typename T, size_t N>
ARR_REF<T,N> ArraySizeHelper(ARR_REF<T,N> arr);

#define arraysize(arr) sizeof(ArraySizeHelper(arr))

Ответ 11

Это довольно старый вопрос, но я собираюсь поставить свои 2 цента, так как есть много ответов, но ни один из них не показывает все возможные методы в ясной и сжатой форме (не уверен в краткой бит, так как это немного пошло из-под контроля. TL; DR 😉).

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

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

Есть несколько способов справиться с этим лучше. Перейдите в std::vector или std::array (не уверен, что std::array был в 2010 году, когда задавался вопрос). Затем вы можете передать объект в качестве ссылки без копирования/перемещения объекта.

std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
    // (before c++11)
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }

    // Note the following are for c++11 and higher.  They will work for all
    // the other examples below except for the stuff after the Edit.

    // (c++11 and up)
    for(auto it = std::begin(arr); it != std::end(arr); ++it)
    { /* do stuff */ }

    // range for loop (c++11 and up)
    for(auto& element : arr)
    { /* do stuff */ }

    return arr;
}

std::vector<int>& fillarr(std::vector<int>& arr)
{
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }
    return arr;
}

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

template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
    // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
    for(int* it = arr; it != arr + N; ++it)
    { /* do stuff */ }
    return arr;
}

Кроме того, это выглядит прикладом уродливым, и супер трудно читать. Теперь я использую что-то, чтобы помочь с тем, чего не было в 2010 году, которое я также использую для указателей функций:

template <typename T>
using type_t = T;

template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
    // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
    for(int* it = arr; it != arr + N; ++it)
    { /* do stuff */ }
    return arr;
}

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

type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

Как я уже сказал, мой трюк type_t<> не работал бы в то время, когда задавался этот вопрос. Лучшее, на что вы могли надеяться, было использовать тип в структуре:

template<typename T>
struct type
{
  typedef T type;
};

typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

который начинает выглядеть довольно уродливым, но, по крайней мере, он еще более читабельен, хотя typename может быть необязательным, тогда в зависимости от компилятора, в результате чего:

type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

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

typedef int(&array5)[5];

array5 fillarr(array5 arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

Тогда свободные функции std::begin() и std::end() не существовали, хотя их можно было легко реализовать. Это позволило бы итерации по массиву более безопасно, поскольку они имеют смысл на массиве C, но не на указателе.

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

void other_function(type_t<int(&)[5]> x) { /* do something else */ }

void fn()
{
    int array[5];
    other_function(fillarr(array));
}

или

void fn()
{
    int array[5];
    auto& array2 = fillarr(array); // alias. But why bother.
    int forth_entry = array[4];
    int forth_entry2 = array2[4]; // same value as forth_entry
}

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

Изменить

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

// separate size value
int* fillarr(int* arr, size_t size)
{
    for(int* it = arr; it != arr + size; ++it)
    { /* do stuff */ }
    return arr;
}

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

// separate end pointer
int* fillarr(int* arr, int* end)
{
    for(int* it = arr; it != end; ++it)
    { /* do stuff */ }
    return arr;
}

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

// I document that this function will ONLY take 5 elements and 
// return the same array of 5 elements.  If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
    for(int* it = arr; it != arr + 5; ++it)
    { /* do stuff */ }
    return arr;
}

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

Вы можете передать std::pair<int*, int*>, который вы можете использовать для начала и конца и передать это, но затем он действительно перестает выглядеть как массив.

std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
    for(int* it = arr.first; it != arr.second; ++it)
    { /* do stuff */ }
    return arr; // if you change arr, then return the original arr value.
}

void fn()
{
    int array[5];
    auto array2 = fillarr(std::make_pair(&array[0], &array[5]));

    // Can be done, but you have the original array in scope, so why bother.
    int fourth_element = array2.first[4];
}

или

void other_function(std::pair<int*, int*> array)
{
    // Can be done, but you have the original array in scope, so why bother.
    int fourth_element = array2.first[4];
}

void fn()
{
    int array[5];
    other_function(fillarr(std::make_pair(&array[0], &array[5])));
}

Забавно, это очень похоже на работу std::initializer_list (С++ 11), но в этом контексте они не работают.

Ответ 12

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

  • Если вы хотите вернуть одномерный массив из функции, вам нужно объявить функцию, возвращающую указатель, как в следующем примере:
int * myFunction()    {
   .
   .
   .
}
  1. С++ не защищает адрес локальной переменной вне функции, поэтому вам нужно будет определить локальную переменную как статическую.

Применяя эти правила к текущему вопросу, мы можем записать программу следующим образом:

# include <iostream>

using namespace std;

int * fillarr( );


int main ()
{

   int *p;

   p = fillarr();

   for ( int i = 0; i < 5; i++ )
       cout << "p[" << i << "] : "<< *(p + i) << endl;

    return 0;
}


int * fillarr( )
{
    static int  arr[5];

    for (int i = 0; i < 5; ++i)
        arr[i] = i;

    return arr;
 }

Выход будет:

p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4

Ответ 13

Просто определите тип [] как возвращаемое значение, например:

        private string[] functionReturnValueArray(string one, string two)
    {

        string[] x = {one, two};


        x[0] = "a";
        x[1] = "b";

        return x;
    }

. , , вызов функции:

string[] y;
y = functionReturnValueArray(stringOne, stringTwo)