int[n][m], where n and m are known at runtime
Solution 1
The feature you are asking about (where the dimensions are only made known at runtime) is a non-standard extension of C++, but a standard one of C.99 (made into an optional feature in C.11). The feature is called variable-length array (VLA), and the link is the documentation for GCC.
If you are using GCC, then you are to pass the length of the array as a parameter to the function.
void foo (int m, int arr[][m]) {
//...
}
However, there seems to be a bug in either the compiler or the documentation, as the above function prototype syntax only works when compiling C code, not C++ (as of gcc version 4.8.2). The only work-around I found was to use a void *
parameter, and cast it int the function body:
int foo_workaround (int m, void *x)
{
int (*arr)[m] = static_cast<int (*)[m]>(x);
//...
}
There are other solutions if you do not want to rely on a compiler extension. If you don't mind a separate allocation for each row, you can use a vector of vectors, for example:
std::vector<std::vector<int> > arr(n, std::vector<int>(m));
However, if you want a single allocation block like you demonstrated in your own example, then it is better to create a wrapper class around vector
to give you 2-d like syntax.
template <typename T>
class vector2d {
int n_;
int m_;
std::vector<T> vec_;
template <typename I>
class vector2d_ref {
typedef std::iterator_traits<I> TRAITS;
typedef typename TRAITS::value_type R_TYPE;
template <typename> friend class vector2d;
I p_;
vector2d_ref (I p) : p_(p) {}
public:
R_TYPE & operator [] (int j) { return *(p_+j); }
};
typedef std::vector<T> VEC;
typedef vector2d_ref<typename VEC::iterator> REF;
typedef vector2d_ref<typename VEC::const_iterator> CREF;
template <typename I>
vector2d_ref<I> ref (I p, int i) { return p + (i * m_); }
public:
vector2d (int n, int m) : n_(n), m_(m), vec_(n*m) {}
REF operator [] (int i) { return ref(vec_.begin(), i); }
CREF operator [] (int i) const { return ref(vec_.begin(), i); }
};
The wrapper's operator[]
returns an intermediate object that also overloads operator[]
to allow 2-dimensional array syntax when using the wrapper.
vector2d<int> v(n, m);
v[i][j] = 7;
std::cout << v[i][j] << '\n';
Solution 2
Why not have an std::vector
of std::vector
's?
std::vector<std::vector<int> > arr(n, std::vector<int>(m));
Accessing an item then becomes:
std::cout << "(2,1) = " << arr[2][1] << std::endl;
cdkrot
Updated on June 06, 2022Comments
-
cdkrot almost 2 years
I often need to create a 2D array with width and height (let them be n and m) unknown at compile time, usually I write :
vector<int> arr(n * m);
And I access elements manually with :
arr[j * m + i]
I recently got told that I could instead write :
int arr[n][m] // n and m still only known at runtime.
So here are 2 questions :
- Is this behaviour allowed by the C++ Standard ?
- How should I pass such an array to a function ? g++ reports that arr has type
int (*)[n]
, but again, n is dynamic and not known outside the function where it is declared (main
).
-
cdkrot over 9 yearsIt is not simple 2D array, it is an array of array. But this solution is also acceptable.
-
cdkrot over 9 years@Jost, if you declare int a[10][10] it will be, internally, int a[100]
-
scohe001 over 9 years@cdkrot please see the first answer on this question
-
cdkrot over 9 yearsyes, that is what i talking about, that the elements are stored in contigious array. About passing in function, answer one on this question should work, but i haven't tested yet.
-
scohe001 over 9 years@cdkrot the
std::vector
ofstd::vector
's will act the same way. Downvoter, care to comment? -
cdkrot over 9 yearsI haven't downvoted anybody, std::vector will use dynamic memory allocation and it will not act this way, it will be like array, containing pointers to other arrays (which may be in other place)
-
scohe001 over 9 years@cdkrot you're entirely right, I misspoke. Good call. Also, that second part wasn't directed at you :)
-
cdkrot over 9 yearsI am using g++, the compilations failing for me (using -std=c++11) for following functions (here just prototypes): int sum(int n, int[][n] m); int sum(int n, int[n][n] m); Reason: "use of parameter outside function body before ] token". What is wrong?
-
jxh over 9 yearsTry
-std=gnu++11
instead. -
jxh over 9 yearsI am not sure I am reading your code correctly. What do you have an extra 'm' before the ')'?
-
cdkrot over 9 yearsm means "matrix", the name of array.
-
cdkrot over 9 yearsOh, not. Moving arr name before [][n] doesn't fixed.
-
jxh over 9 yearsYeah, it seems the documentation for passing to functions only works for C code, not for C++ code. It is either a compiler bug or documentation bug.
-
cdkrot over 9 yearsYes, thank you for your answer, I filed a bug on gcc's bugzilla, i will write you, when they answer.
-
jxh over 9 years@cdkrot: You are very welcome. This is likely because there is no C++ mangling conventions for a VLA argument, so the C++ front end refuses to accept the syntax. Wrapping the function around an
extern "C"
didn't help either, though, so there might be room to fix something. -
jxh over 9 yearsRegarding VLA and C++ proper, see this related post.