开发者

What is the best way to create a sub array from an exisiting array in C++?

OK, I am trying to get a sub array from an existing array and I'm just not sure how to do it. In my example I have a very large array, but I want to create an array from the last 5 elements of the array.

An example of what I am talking about would be:

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

I know this isn't correct, but I am having some trouble getting it rig开发者_开发技巧ht. I want to get the elements 96 through 100 in array1 and put them into array2 but I don't want to copy the arrays. I just want array2 to start at the 96 element such that array1[96] and array2[0] would be pointing to the same location.


for this:

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

you can do:

int *arr2 = arr1 + 96;
assert(arr2[0] == arr1[96] == 97);


A reference hack from a C programmer willing to subvert the type system to get what works:

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

Now array2 will be an array for all intents and purposes, and will be a sub-array of array1, and will even be passable to that famous C++ array_size template function. Though the best way to handle this hackery is to hide it with more hackery!

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

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

Nice. Terrible by some standards, but the end result a) looks pretty neat, b) does exactly what you want, c) is functionally identical to an actual array, and d) will also have the added bonus (or mis-feature) of being an identical reference to the original, so the two change together.

UPDATE: If you prefer, a templated version (sort of):

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);

We still have to pass the type. Since one of our arguments must be used as part the cast, we cannot cleanly (IMHO) avoid the macro. We could do this:

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);

But the goal here is to make the calling code as clean as possible, and that call is a bit hairy. The pure-macro version probably has the least overhead and is probably the cleanest to implement in this case.


You can use boost::iterator_range to represent "slices" of arrays/containers:

#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";
}

Note that the iterator range "knows" about the size of the sub-array. It will even do bounds checking for you. You cannot get that functionality from a simple pointer.

The usefulness of Boost.Range will become more apparent once you learn about STL containers and iterators.

If you're into linear algebra, Boost.uBlas supports ranges and slices for its matrices and vectors.


For a completely different approach you could do something like.

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

or

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

This would make a real copy of the elements of your vector.


In C++ you can use an int pointer as an int array, so getting the array2 to start at item 96 in array1 is easy, but there isn't any way to give array2 a size limit, so you can do this

int array2[] = &array1[96];

or this

int *array2 = &array1[96];

but NOT this

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

On the other hand, C++ doesn't enforce array size limits anyway, so the only real loss is that you can't use sizeof to get the number of elements in array2.

note: &array1[96] is the same thing as array+96

edit: correction - int array[] = &array[96] isn't valid, you can only use [] as a synonym for * when declaring a function parameter list.

so this is allowed

extern int foo(int array2[]);
foo (&array1[96]);


int array1[] = {1,2,3,...99,100};
int *array2 = &array1[96];


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;

The code is not safe if the boundaries are not handled properly.


You said you don't want to copy the array, but get a pointer to the last five elements. You almost had it:

int array1[] = {1,2,3,...99,100};
int* array2  = &array1[95];
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜