I have 2D array of B-class objects in class A (as a private member of
this class):
class A {
private:
B array[16][16];
//...........
}
and I want to access this array outside this class (only for reading
values). So I have a method in class A which returns the poiner to the
array:
return (B**) array;
and I use this method outside the class A:
A a;
// some operations on a and other objects...
B ** b = a.getArray();
But the program crashes if I try to read anything from the b, e.g:
resFile << b[1][2].u;
int u is a public member of class B.
What is wrong?
The problem is not with the resFile object (there are some lines
written in the file before the program's crash). People, please, help
me!
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Whilst you can access a 1D array via a pointer you cannot access
elements of a 2D array with a pointer to a pointer. Only the top level
decays to a pointer so b[n][m] is an array of n arrays of array of m B's
That can be accessed through a pointer to an array of m Bs.
>
> int u is a public member of class B.
> What is wrong?
>
> The problem is not with the resFile object (there are some lines
> written in the file before the program's crash). People, please, help
> me!
>
The best answer is to stop using raw arrays and learn to use std::vector.
Thanks for your advice, however, I still would like to know how to
return a pointer/reference to a 2D array...
MR
You can't.
Arrays decay to a pointer to their first element, which in case of
B array[16][16];
means
B(*)[16].
This is not convertible to B**, for fairly obvious reasons (which
require understanding what arrays are, objects put in memory one after
the other).
Alternatively you could choose to return a B* that you can obtain with
&array[0][0], and then do p[j+i*16] to access array[i][j].
int TwoD[10][5]; // TwoD is a ten element array of five element
array.
int (*TwoDPtr)[5]; // TwoDPtr is a pointer to a five element array.
TwoDPtr = TwoD; // OK.
If you're trying to do something with an arbitrary "second" dimension,
you're going to have to manage that yourself.
C++ doesn't handle arbitrary sized arrays very well (well not at all,
really). For 1d,
there's std::vector. For 2d you'll have to go slightly outside the
language to the boost::matrix class or something along those lines (or
implment it yourself).
As Francis said in his reply, only the top level of an array decays
to a pointer, so you might want to implement your getArray function
as something like:
B * getArray() { return &array[0][0]; }
Then, in order to use this pointer in code that calls getArray to
access elements of the array, you will need to think about how C++
stores multi-dimensional arrays.
If you think of a 2-d array in terms of rows and columns, you
might visualize, say:
int array[3][5]
something like this:
cols
00 01 02 03 04
rows 10 11 12 13 14
20 21 22 23 24
However, if my understanding is correct, C++ will store this as
a contiguous series of ints as follows:
(row 0) (row 1) (row 2)
| 00 01 02 03 04 | 10 11 12 13 14 | 20 21 22 23 24 |
^ ^ ^
| | |
-----------------------------------
|
(col 0)
Given a pointer to the first element, which in this example is had
from:
int *p_arr = &array[0][0];
you can then access elements using the equivalence:
array[2][3] == p_arr[2 * 5 + 3];
or, more generally, for
int array[ROW_SIZE][COL_SIZE];
array[i][j] == p_arr[i * COL_SIZE + j];
// 0 <= i && i < ROW_SIZE; 0 <= j && j < COL_SIZE
To attempt to apply this to your specific example - for which you
only supply snippets, so much of this is necessarily filled in by
guesswork - you might think in terms of something like the
following:
#include <iostream>
const int ROW_SIZE = 16;
const int COL_SIZE = 16;
class B {
public:
int u;
B(int u = 0): u(u) { }
};
class A {
private:
B array[ROW_SIZE][COL_SIZE];
public:
A() {
for (int i = 0; i < ROW_SIZE; ++i)
for (int j = 0; j < COL_SIZE; ++j)
array[i][j] = B(i + j);
}
B * getArray() { return &array[0][0]; }
};
int main()
{
A a;
B *b = a.getArray();
int i = 2; // 0 <= i && i < ROW_SIZE
int j = 5; // 0 <= j && j < COL_SIZE
std::cout << "array[i][j].u == "
<< b[i * COL_SIZE + j].u << '\n';
return 0;
}
However, again like Francis, I would ordinarily avoid the
complications of working with multi-dimensional arrays in this
way in favour of std::vector, replacing your
B array[16][16];
with something like:
std::vector<std::vector<B> > array(16, std::vector<B>(16));
Because of this I have little hands-on experience of working with
arrays in the way you are attempting, so I should say that my
suggestion above _requires_ that C++ _always_ implements a 2-
dimensional array using contiguous storage that allows it to
be accessed "as if" it were a single-dimensional array using
the syntax:
array_ptr[row * COL_SIZE + col]
above. For this to work in an implementation-independent way
(as it works using gcc-4.3.2), perhaps someone could clarify
whether this is indeed guaranteed by the standard?
regards
Paul Bibbings
Use typedef. The following works. (However, I recommend
a "const yourclass& operator()(unsigned,unsigned)" function instead
of returning the actual array.)
--Jonathan
// -----------------------------------------------------------------
#include <iostream>
typedef const int arrayro[16][16];
class A {
public:
A() {
for (int i = 0; i < 256; ++i)
B[i / 16][i % 16] = 0;
}
arrayro& getArray() {
std::cout << "B address: "
<< B << std::endl;
return B;
};
int& operator()(unsigned i, unsigned j) {
if (i >= 16 || j >= 16) throw 1;
return B[i][j];
}
private:
int B[16][16];
};
int main() {
A a;
arrayro& b = a.getArray();
std::cout << "b address: " << b << std::endl;
a(12, 1) = 5;
std::cout << b[12][1] << std::endl;
return 0;
> Because of this I have little hands-on experience of working with
> arrays in the way you are attempting, so I should say that my
> suggestion above _requires_ that C++ _always_ implements a 2-
> dimensional array using contiguous storage that allows it to
> be accessed "as if" it were a single-dimensional array using
> the syntax:
>
> array_ptr[row * COL_SIZE + col]
>
> above. For this to work in an implementation-independent way
> (as it works using gcc-4.3.2), perhaps someone could clarify
> whether this is indeed guaranteed by the standard?
Yes, it works fine iff the array is statically declared. If it is a
dynamic array of arrays then the array elements are unlikely to be
contiguous though the elements of each inner array are required to be.
int a[4];
a[i] = 0;
This is an array of 16 ints. If you want a pointer to such an array,
int (*p)[4] = &a;
(*p)[i] = 0;
this is a pointer to an array of 16 ints. But you probably don't
want this, because the size of the array is embedded in the type and
this makes it less flexible; you need different types for array of
different sizes. What's much more common is:
int *p = a;
p[i] = 0;
this is a pointer not to the whole array, but the initial element
of an arbitrary-sized array of ints.
Now, suppose you have a (so-called) 'multi-dimensional' array:
int a[4][3][2];
a[i][j][k] = 0;
this is an array of 4 ( arrays of 3 arrays of 2 ints ). A pointer to
such an array can be obtained like this:
int (*p)[4][3][2] = &a;
(*p)[i][j][k] = 0;
but this is not common, for the same reason as above. You can decay
the first dimension of the array into a pointer:
int (*p)[3][2] = a;
p[i][j][k] = 0;
where p is a pointer to an array of 3 arrays of 2 ints. But you can do
this only on the first dimension. Why? Because the pointer needs the
complete type of what it points to to perform its operations (addition,
subtraction and dereference), and as soon as you lose the second
dimension, you also lose the type of what the above pointer points to.
A pointer to an array of an unknown size is not very useful.
If the dimensions (except the first) are all compile-time constants,
then you can use this pointer type just like an array. No problem.
On the other hand, you can decay the second dimension of an array
into a pointer if you use it only within a specific subarray, i.e.
with the first dimension fixed, and don't need a pointer to the
outer array:
int (*p)[2] = a[i];
p[j][k] = 0;
because the type of what p points to is completely known. Likewise,
you can do the same thing to the third dimension if you use it only
within a specific subarray, i.e. with the first and the second
dimensions fixed:
int *p = a[i][j];
p[k] = 0;
To implement a multi-dimensional array with its dimensions that are
known only at run-time, you can't have an array of arrays, but you
need an intermediate layer: you need an array of *pointers to* the
first elements of the subarrays.
+---+
| | +---+---+---+
| *-+--->| | | |
+---+ +---+---+---+
| | +---+---+---+
| *-+--->| | | |
+---+ +---+---+---+
| | +---+---+---+
| *-+--->| | | |
+---+ +---+---+---+
| | +---+---+---+
| *-+--->| | | |
+---+ +---+---+---+
(Since the subarrays are independent of each other, you can even have
a "ragged" array: a[0] is an array of 3 ints, a[1] an array of 5 ints,
and so on..)
--
Seungbeom Kim
This could be misinterpreted by him. What you want to say, i think is
new int*[M] { /* set inner pointers in a loop... */ } (this is really
more like an array of pointer to arrays, though). What he could have
understood is new int[N][M], which of course is contiguous too.
Just be aware that this is undefined behavior if the index goes above
16. I'm not aware of a case where this actually causes bad things to
happen, though. (alias analysis or something like that?). Maybe
someone could elaborate on this?
On Jun 28, 7:12 am, Marcin Rodzik <marteno_ro...@o2.pl> wrote:
> > The best answer is to stop using raw arrays and learn to use std::vector.
>
> Thanks for your advice, however, I still would like to know how to
> return a pointer/reference to a 2D array...
While using a std::vector of std::vector's is certainly a possibility,
there are a few slight (and possibly disadvantageous) differences
between this and the raw C array approach. First of all, the 'rows'
of your 'matrix' are no longer contiguous in memory with each other,
even though the elements are still contiguous within the same row.
Second, C arrays have the option of being stack allocated, while the
contents of a vector do not. Granted, this has the advantage of
allowing you to dynamically resize the vector, but based on the
initial post (using a 16 x 16 matrix), I'm assuming that isn't
necessary.
If either of the issues mentioned above are relevant to your current
task at hand, you can write a thin wrapper class around C arrays that
allows you pass multi-dimensional arrays as parameters, as well as
return them by value, reference, or pointer. Compared to dealing with
raw pointers, the interface is much cleaner but the assembly output
should still be the same.
// note: 'Static' might not be the best word choice, with respect to the
// class name; in this case, I simply mean non-dynamic & fixed size
// copy constructors and operator = need to be implemented, depending on
// the POD-ness of type T
template <typename T, int N> class StaticArray
{
private:
T array[N];
public:
const T& operator [](int i) const
{
return array[i];
}
T& operator [](int i)
{
return array[i];
}
};
To use this class as a two-dimensional array function parameter:
void foo(const StaticArray<StaticArray<B, 16> >& matrix)
{
for(int i = 0; i < 16; i++)
{
for(int j = 0; j < 16; j++)
{
B bar = matrix[i][j];
// do stuff
}
}
}
Similarly, you can also return a 'StaticArray<StaticArray<B, 16> >' by
value, const reference, etc. You should probably avoid the overhead
of passing/returning by value unless you actually need a separate copy
of the array.
As has already been discussed, you don't want a pointer to an array of
pointers to objects, you want a pointer to an array of arrays of your
objects, because that's how they are laid out in memory.
To make a pointer, which you can return from a function, signify this
you need to define a type - i.e. typedef.
Here's an illustration with a template class. Here the the A<T>
template class defines typedef A<T>::A16 which is an array of 16 Ts.
The client code in main() instantiates A<B> and thus returns a pointer
to an array of arrays of 16 Bs.
--------8<--------
#include <iostream>
class B;
std::ostream& operator<<(std::ostream&,const B&);
class B {
static int instances;
int instance;
friend std::ostream& operator<<(std::ostream&,const B&);
public:
B() : instance(++instances) {
}
};
int B::instances = 0;
std::ostream& operator<<(std::ostream& os,const B& b) {
return os << "B#" << b.instance;
}
template<typename T> class A {
T array[16][16];
public:
typedef T A16[16];
const A16* const getArray() const {
return array;
}
};
int main()
{
A<B> a;
for (int k = 0;k < 2;++k) {
const A<B>::A16* const p = a.getArray();
for (int i = 0;i < 16;++i)
for (int j = 0;j < 16;++j)
std::cout << p[i][j] << ',';
std::cout << std::endl;
}
}
--------8<--------