Передача массивов в качестве ссылки - PullRequest
5 голосов
/ 26 октября 2011

В C ++, как я могу передать массив как ссылку , когда я не знаю размер во время компиляции? До сих пор я узнал, что единственный способ заставить это работать - использовать что-то вроде

const double( &numbers ) [size]

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

Мои вопросы:

  1. Если я не передаю массив как ( const double( &numbers ) [length] ), потому что, например, я не знаю его размер, как мне убедиться, что он не скопирован , но это ссылка
  2. Если я передам массив, как в приведенном выше примере, ( double array[] ) это ссылка или копирование ?

Ответы [ 6 ]

6 голосов
/ 27 октября 2011

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

void f( double const numbers[], std::size_t length )
{ ... }

template< std::size_t Length >
void f( double const (&numbers)[ Length ] )
{
    return f( numbers, Length );
}
6 голосов
/ 26 октября 2011

В C ++ вы должны использовать std :: vector .

В C / C ++ нельзя передавать массивы как копию. Массивы всегда передаются по ссылке.

EDIT

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

2 голосов
/ 26 октября 2011

Если я не передам массив как (const double (& numbers) [length]), потому что, например, я не знаю его размер, как мне убедиться, что он не копируется, но на него ссылаются?

Да, это означает, что вы передаете массив как ссылку,

void Foo(const double( &numbers ) [length]);

Обратите внимание, что length является постоянным целым числом.

Если я передам массив, как в приведенном выше примере, (двойной массив []) это ссылка или это скопировано?

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

void Foo(const double *length);
2 голосов
/ 26 октября 2011

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

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

Если вы не знаете размер вашего массива во время компиляции, просто используйте (нормальный) указатель на ваш тип данных вместо явного массива,То есть, T my_array[] (где T - это тип, например int, double или даже один из ваших классов) становится T* my_array, и синтаксис в дальнейшем будет таким же, как ... my_array[i] будетработают нормально (другой синтаксис также существует, но не такой элегантный).Для инициализации используйте оператор new:

T* my_array;

my_array = new T[3];

или

T* my_array;

my_array = new T[x];

, где x - целое число (не обязательно постоянное, как в случае с обычными массивами).Таким образом, вы можете взять это x у пользователя во время выполнения и затем создать свой «массив».Просто позаботьтесь о том, чтобы не забыть delete[] my_array после окончания его использования, чтобы избежать утечек памяти.

[Заключительное примечание] Использование такого массива с динамическим размещением - хороший выбор, только если вы точно знаетесколько элементов вы хотите ... или во время компиляции или даже во время выполнения.Так, например, если после того, как пользователь введет x, вы точно будете его использовать, это нормально.В противном случае вы столкнетесь с опасностью переполнения массива (если вам нужно больше x), что обычно приводит к сбою приложения, или просто к потере некоторого пространства.Но даже если это так, вы сами реализуете большинство функций, необходимых для манипулирования массивами.Вот почему предпочтительно использовать контейнеры, предоставляемые стандартной библиотекой C ++, например std::vector (как упоминалось Donotalo ).Я просто хотел подробнее остановиться на этом вопросе.

2 голосов
/ 26 октября 2011

Пара вещей:

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

  2. Когда вы передаете массив, это делается по ссылке. Не копируется.

В любом случае, вы, возможно, захотите использовать vector.

РЕДАКТИРОВАТЬ: см. Комментарии.

double average(const double *arr, size_t len){
    //  Compute average
    return accumulate(arr, arr + len, 0) / (double)len;
}

int main(){

    double array[10] = //  Initialize it

    cout << average(array, 10) << endl;

    //  Alternatively: This could probably be made a macro.
    //  But be careful though since the function can still take a pointer instead
    //  of an array.
    cout << average(array, sizeof(array) / sizeof(double)) << endl;

    return 0;
}
0 голосов
/ 26 октября 2011

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

Всякий раз, когда в вашем коде есть массив фиксированного размера, компилятор знает размер массива, так как он читает его из самого исходного кода.Но как только код скомпилирован, информация о длине теряется.Например:

void f1(double array[10]) {...}

Компилятор не будет применять размер массива.Следующий код будет автоматически скомпилирован, поскольку параметр массива f1 использует просто указатель на первый элемент массива:

void h1() {
    double a[10];
    double b[5];

    f1(a); // OK.
    f2(b); // Also OK.
}

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

void f2(double array[], size_t array_size) {...}

Затем вы можете вызвать эту функцию с любым массивом:

void h2() {
    double a[10];
    double b[19];

    f2(a, sizeof(a) / sizeof(a[0]));
    f2(b, sizeof(a) / sizeof(a[0]));
}

Параметр array_size содержит фактический размер массива.

Как примечание, sizeof(array) работает только со статически определенными массивами.Когда вы передаете этот массив другим функциям, информация о размере теряется.

Массив не совпадает с указателем.Однако массив неопределенного размера, такой как параметр f2, является просто указателем на первый элемент double в последовательности:

void f3(double array*, size_t array_size) {...}

Для любых практических целей, f2 и f3эквивалентны.

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

void g(std::vector& v) {...}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...