Каков наилучший способ создания подмассива из существующего массива в C ++? - PullRequest
10 голосов
/ 26 января 2010

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

Примером того, о чем я говорю, будет:

int array1 = {1,2,3,...99,100};
int array2[5] = array1+95;

Я знаю, что это не правильно, но у меня возникли некоторые проблемы с правильным решением. Я хочу получить элементы с 96 по 100 в массиве 1 и поместить их в массив 2, но я не хочу копировать массивы. Я просто хочу, чтобы array2 начиналась с элемента 96, чтобы array1 [96] и array2 [0] указывали на одно и то же местоположение.

Ответы [ 8 ]

18 голосов
/ 26 января 2010

для этого:

"such that array1[96] and array2[0] would be pointing to the same location."

Вы можете сделать:

int *arr2 = arr1 + 96;
assert(arr2[0] == arr1[96] == 97);
9 голосов
/ 26 января 2010

Ссылочный взлом от программиста C, желающего подорвать систему типов, чтобы получить то, что работает:

int (&array2)[5] = (int (&)[5])(*(array1 + 5));

Теперь array2 будет массивом для всех намерений и целей и будет подмассивом array1, и даже будет передаваться этой известной шаблонной функции C ++ array_size. Хотя лучший способ справиться с этой хакерской атакой - это спрятать ее еще больше!

#define make_sub_array(type, arr, off, len) (type (&)[len])(*(arr + off));

int (&array2)[5] = make_sub_array(int, array1, 5, 5);

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

ОБНОВЛЕНИЕ: Если вы предпочитаете, шаблонную версию (вроде):

template <typename T, size_t M>
T (&_make_sub_array(T (&orig)[M], size_t o))[]
{
    return (T (&)[])(*(orig + o));
}
#define make_sub_array(type, array, n, o) (type (&)[n])_make_sub_array(array, o)

int (&array2)[5] = make_sub_array(int, array1, 5, 5);

Нам еще нужно передать тип. Поскольку один из наших аргументов должен использоваться как часть приведения, мы не можем чисто (IMHO) избежать макроса. Мы могли бы сделать это:

template <typename T, size_t M, size_t N>
T (&make_sub_array(T (&orig)[M], size_t o))[N]
{
    return (T (&)[N])(*(orig + o));
}

int (&array2)[5] = make_sub_array<int, 15, 5>(array1, 5);

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

3 голосов
/ 26 января 2010

Вы можете использовать boost :: iterator_range для представления «кусочков» массивов / контейнеров:

#include <iostream>
#include <boost/range.hpp>

int main()
{
    int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    // Create a "proxy" of array[5..7]
    // The range implements the concept of a random sequence containter
    boost::iterator_range<int*> subarray(&array[5], &array[7]+1);

    // Output: 3 elements: 5 6 7
    std::cout << subarray.size() << " elements: "
              << subarray[0] << " "
              << subarray[1] << " "
              << subarray[2] << "\n";
}

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

Полезность Boost.Range станет более очевидной, когда вы узнаете о контейнерах и итераторах STL.

Если вы знакомы с линейной алгеброй, Boost.uBlas поддерживает диапазоны и срезы для своих матриц и векторов.

3 голосов
/ 26 января 2010

Для совершенно другого подхода вы могли бы сделать что-то вроде.

vector<int> v0(array1 + 95, array1 + 100);

или

vector<int> v1(array1, array1 + 100);
vector<int> v2(v1.begin() + 95, v1.end());

Это создаст реальную копию элементов вашего вектора.

1 голос
/ 26 января 2010

В C ++ вы можете использовать указатель int в качестве массива int, поэтому заставить массив array2 начинаться с элемента 96 в array1 легко, но нет никакого способа задать для array2 ограничение размера, так что вы можете сделать это

int array2 [] = & array1 [96];

или это

int *array2 = &array1[96];

но НЕ это

int array2[5] = &array1[96]; // this doesn't work.

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

примечание: &array1[96] - это то же самое, что и array+96

правка: исправление - int array[] = &array[96] недопустимо, вы можете использовать [] как синоним для * при объявлении списка параметров функции.

так что это разрешено

extern int foo(int array2[]);
foo (&array1[96]);
0 голосов
/ 26 января 2010
int arr[] = { 1, 2, 3, 4, 5};
int arr1[2];
copy(arr + 3, arr + 5, arr1);
for(int i = 0; i < 2; i++)
    cout << arr1[i] << endl;

Код не является безопасным, если границы не обрабатываются должным образом.

0 голосов
/ 26 января 2010
int array1[] = {1,2,3,...99,100};
int *array2 = &array1[96];
0 голосов
/ 26 января 2010

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

int array1[] = {1,2,3,...99,100};
int* array2  = &array1[95];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...