views:

327

answers:

8

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 right. 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.

+7  A: 

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);
jspcal
That `assert` should be a mathematician asserting, not C :-)
Alok
this works! Thank you!
John
+1 for the simple solution that works.
Chris Lutz
A: 

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];
BlueRaja - Danny Pflughoeft
when I tried that I got this error:cannot convert from 'int *' to 'int []'
John
`int array2[] = array1+95;` is illegal.
Alok
Should be int array1[] = {1,2,3,...99,100}; int* array2 = array1+95;You cannot initialize an array (square brackets) with a pointer -- it can tell where it starts but not its size.
BennyG
+1  A: 
John Knoeller
John
@John: `int array2[] = ` is bad.
Alok
Yeah, `int foo[]` and `int *foo` are interchangeable only as function parameters.
jamesdlin
@james: They aren't really interchangeable. Rather, arrays can *decay* into pointers, but they aren't the same.
GMan
@GMan: They are interchangeable as function *parameters* (not *arguments*). That's unrelated to the decay thing.
jamesdlin
A: 
int array1[] = {1,2,3,...99,100};
int *array2 = &array1[96];
Judge Maygarden
A: 
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.

Jagannath
+2  A: 

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.

Chris Lutz
It's so bad it's good :-). +1.
Alok
Thanks. It took me much longer because I wanted to make it work with actual C++ casting, but eventually I gave up and made it _work_. Improvements (especially a potential version of `make_sub_array` as a template function - I tried that for a while as well before giving up) welcome.
Chris Lutz
@Chris: My C++-fu isn't great, I am more of a C person, but I will try!
Alok
That's slick... er ugly... I mean, I'm impressed. The macro helps readability, but the declaration of array2 is still sort of weird. +1 for the fu
John Knoeller
I could make the macro `#define make_sub_array(type, orig, new, off, len) type (` but I would (personally) prefer to see the assignment in my code so that it's clear that we're declaring and defining a variable.
Chris Lutz
Erm. Ok. Like obviously some people before me, my brain is now totally fucked up. Can someone give me a good location for reading up not on the specific template solution but on the problem in general. Google gave me a steganographic result. Can this really find the size of an array that I pass to a function? (Which would be the most reasonable appliance I can think of)
AndreasT
`template <typename T, size_t N> size_t array_size(T ( }` is the template function `array_size` that returns the size of an array, regardless of type (or size). It doesn't find the size of the array per-se. It takes a reference to an array of a set size. The template just allows that size to vary. We end up not really needing the array itself for anything other than determining the template arguments. See also: http://stackoverflow.com/questions/437150/can-someone-explain-this-template-code-that-gives-me-the-size-of-an-array
Chris Lutz
+2  A: 

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.

lhahne
Should be `std::vector` for pendantry. Anyway, +1 for the most "C++" answer.
Chris Lutz
+1  A: 

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.

Emile Cormier