2

I'm somewhat new to programming in general and I've run into an issue with declaring 3D and 4D arrays. I have several declarations like this at the start of my main function, but I've narrowed the problem down to these 4:

string  reg_perm_mark_name[64][64][64];
short   reg_perm_mark_node_idex[64][64][64];
short   reg_perm_mark_rot[64][64][64][4];
short   reg_perm_mark_trans[64][64][64][3];

When I run my program with these, I get "System.StackOverflowException" in my executable. I would much prefer a way to allocate them dynamically, The way I have it now was meant to be temporary anyway and I'm not sure how to declare array pointers properly.

The 4 elements I'm using in the 4D array reg_perm_mark_trans, for example, are [node index][region index][marker index][xyz coordinates]. Also there's a total of 35 multidimensional arrays being declared at once. (most of them are 1D and 2D) I'm not sure if that helps.

Can someone show me how to make these 4d arrays work or maybe how to make them dynamically allocating with pointers or vectors? Be descriptive please, I'm still learning.

10
  • 2
    The space for such "simple" variables is intentionally very limited. Your arrays are too big. Use a vector etc. (which uses new inside, this isn´t limited.). There are enough examples how to use std::vector in the Internet. Commented Jul 11, 2015 at 1:06
  • Standard containers are a poor substitute for multidimensional arrays, though. Commented Jul 11, 2015 at 1:08
  • 1
    @celticminstrel std::array is a great substitute for naked multidimensional arrays. Same goes for std::vector when you're working with dynamically sized multidimensional arrays. Commented Jul 11, 2015 at 1:27
  • Oh, you have a point - std::array at least is a decent substitute for multidimensional arrays. Not so much with std::vector though - you have to choose between manually flattening the index, or using a "jagged array" instead of a true multidimensional array. Commented Jul 11, 2015 at 1:59
  • 1
    Please note the dynamically sized portion of my comment on std::vector. You'll have even more overhead trying to manage a dynamically sized multidimensional array yourself than you will using std::vector. The really nice thing is you can mix and match them with the greatest of ease so you get the best of both worlds without all the overhead and headache you get by dismissing the usefulness of standard containers. Commented Jul 11, 2015 at 2:04

3 Answers 3

4

Assuming for simplicity that sizeof(string) == 2 (it's probably more), you're trying to allocate (64^3)*9*2 bytes on the stack. That comes out to 4,718,592 bytes, or approximately 4.5 MiB. Most likely, you just don't have 4.5 MiB available on your stack.

Since these variables are declared in main(), you have two possible solutions:

  1. Declare them static.

  2. Declare them outside main(), as global variables.

This will cause them to be allocated before the program starts, rather than on the stack. The only difference between the two approaches is whether they'll be visible in other functions.

There may also be a way to tell your compiler that the program needs more stack space, but I think making them static is probably the better solution here. If they were in a function other than main() though, you'd probably need to do something else.

Sign up to request clarification or add additional context in comments.

7 Comments

Something is very wrong about your byte calculation. It´s more like MB, not GB. And where did that 9 come from? Btw. a std::string can´t use less than pointer (and in reality, depending on the implementation, it´s far more)
...you're right. It's MiB, not GiB. And the 9 comes from the sum of his fourth dimensions (4 and 3) plus the fact that the other two are three-dimensional. I'm only estimating sizeof(string) == 2 because it simplified the calculation by making all the base types the same size. It's an estimate only.
There are plenty of contexts where you can't declare variables static or make them global (e.g multi-threaded) and you would need to allocate dynamically.
I won't down vote because the answer isn't wrong, and it's reflects effort, and I hate down voting, but this really isn't the best answer. Putting giant arrays into static storage is hardly idiomatic. Also, creating non function-local static variables of non trivial type is incredibly dangerous; code written like this calls constructors before main. Constructors before main can cause innocent code to segfault depending how things are linked, and is banned in many style guides (e.g. llvm).
string is a class so it's size must be much larger than 4 bytes because it must store string length along with allocated memory
|
2

A simple solution is to use static allocation (i.e. move your arrays outside of any function, or mark them as static).

Note that if you use C++ arrays then the usage and footprint are the same but then they behave like proper containers:

array<array<array<string,64>,64>,64> reg_perm_mark_name;

To use dynamic allocation unsafely, you could write:

auto reg_perm_mark_rot = new short[64][64][64][4];
// ...
delete[] reg_perm_mark_rot;

To use it safely, since C++14 (note that the innermost dimension gets special treatment):

auto reg_perm_mark_rot = std::make_unique<short[][64][64][4]>(64);

Of course you can use array instead of C-style arrays with the dynamic options, but then you would have an extra level of indirection to use the array.

9 Comments

There's no extra level of indirection between a c array and a std array. You should use std::array.
Also, please never tell people to use globals of non trivial type, it's wildly dangerous, see my other comment.
@NirFriedman there is if you are dynamically allocating it. If you disagree then please show your code.
These global strings are not dangerous at all, let alone wildly. The standard requires that they are initialized before first use (unless another unit accesses them via extern, which I'm not suggesting)
It is in fact dangerous, because depending on the linking structure of the project, they can get double destructed at exit. See e.g. stackoverflow.com/questions/6714046/…. If you write out a trivial program, you will see such variables construct before main and destruct after, this turns out to not be very robust in the face of linking pieces of code together.
|
2

This line:

string  reg_perm_mark_name[64][64][64]

declares 64*64*64 = 262144 strings on the stack. A std::string is typically about 32 bytes so thats about 8MB. The maximum stack size is typically about 1MB.

To declare the array dynamically you could use std::vector. Generally, multidimensional std::vectors can be a bit cumbersome and it is often better to declare a single dimensional vector and convert to a single index when you access an element:

std::vector<std::string> reg_perm_mark_name(64*64*64);

int i = 13;
int j = 27;
int k = 7;
reg_perm_mark_name[i + 64*j + 64*64*k] = "Hello world!";

But in this case you can declare a multi-dimensional std::vector quite efficiently by using std::array instead of std::vector for the inner types. The use of std::array avoids too many memory allocations as they have a fixed size. I would use typedefs or using aliases to make the declaration clearer:

using StrArray = std::array<std::string, 64>;
using StrArray2D = std::array<StrArray, 64>;

std::vector<StrArray2D> reg_perm_mark_name(64);

reg_perm_mark_name[3][4][7] = "Hello world!";

7 Comments

It's completely irrelevant whether the inner types fit on the stack. If the outer type is a vector, everything except that one vector (3 words usually) will be on the heap. In fact, if the outer most type is a vector but all the inner types are array, you get the best of everything: single allocation, almost no overhead, no possibility of stack over flow, and nice indexing notation.
@NirFriedman Yeah, I've used the wrong term. I was just trying to say the inner types are of suitable size to be chunks for allocation. I'll try and edit to make it more accurate...
The best answer, hope it overtakes the other two as they have serious issues.
Well, even though I have my own answer, this one's worth an up-vote. I'm a bit dubious about his second option, mind you. I'd instead suggest writing a wrapper class (though that's a fair bit of work even for 2D, and even more for 3D and 4D.)
Why is the second option dubious?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.