Multi-dimensional Array Initialization [C++] (25)

1 Name: #!/usr/bin/anonymous : 2007-10-30 16:30 ID:32FLFmjy

I'm trying to relearn C++ by writing a program that manipulates matrices and blah blah blah moving from Java. So far I have this:

[snip]

class Matrix
{
private://all private members get a pr_* prefix to distinguish them
int pr_rows, pr_cols;
double pr_Matrix[0][0]
bool pr_isSquare() { return (pr_rows == pr_cols); }
public:
Matrix(int, int);

[snip]

};
Matrix::Matrix(const int rows, const int cols)
{
pr_rows = rows;
pr_cols = cols;
pr_Matrix = new double[pr_rows][pr_cols];
}

[snip]

When I try to compile, I get
"error: 'Matrix::pr_cols' cannot appear in a constant expression"

Can someone help me with this? I've tried Google, and I think I understand what's causing the problem, but I don't know how to work around it.

2 Name: dmpk2k!hinhT6kz2E : 2007-10-30 18:36 ID:Heaven

Since it's just doubles, have you tried "new double[pr_rows * pr_cols];"?

3 Name: dmpk2k!hinhT6kz2E : 2007-10-30 18:39 ID:Heaven

Wait, that probably won't work. I remember some hackery similar to this in C, and is wasn't all that pretty.

You end up with an array of pointers to arrays of doubles. This is because the first array doesn't know what its dimension is at compile time.

Also, "[0][0]" is probably a bad idea.

4 Name: #!/usr/bin/anonymous : 2007-10-30 19:34 ID:32FLFmjy

>>3

>Also, "[0][0]" is probably a bad idea.

Yeah, probably, but the compiler was bitching about not knowing the dimensions of the array, so I just gave it some dummy values so it would shut up. It doesn't seem to be hurting anything, but who knows...

5 Name: #!/usr/bin/anonymous : 2007-10-30 19:36 ID:32FLFmjy

>array of pointers to arrays

That'd be a double***, wouldn't it?

6 Name: dmpk2k!hinhT6kz2E : 2007-10-30 21:02 ID:Heaven

I'd use "double *prMatrix[]".

The trick is to get something like "x = prMatrix[y][z]" to work like you'd expect. Normally it'd be something along the lines of "x = prMatrix[y]->[z]" if you use an array of pointers to an array (IIRC). It's possible to get the first form, but I don't recall how.

7 Name: #!/usr/bin/anonymous : 2007-10-31 00:40 ID:M740Z140

>>6

x = prMatrix[y]->[z] is never valid syntax. Array lookups are only ever done with [], no matter if you're dealing with pointers or arrays. Syntactically, arrays and pointers are nearly indistinguishable.

Anyway, >>1, what you want to do is something along the lines of (skipping all the class syntax):

double **array;
array=new (double *)[rows];
for(int i=0;i<rows;i++) array[i]=new double[cols];

Then you can address it as array[row][col]. Remember to loop through it and free all the sub-arrays when you free the whole thing!

If you want it a bit more efficient, allocate a buffer of rows*cols and just put pointers to various offsets into that buffer into array.

8 Name: #!/usr/bin/anonymous : 2007-10-31 00:42 ID:M740Z140

Alternatively, just drop the multi-dimensional array and access the element at row,col as buffer[row+col*rows] where buffer is an array of size rows*cols. There are some minimal differences in performance between the two, but pick whichever one seems more comfortable.

9 Name: #!/usr/bin/anonymous : 2007-10-31 07:51 ID:AkEOQ1Xr

>>8
I'd go as far as to say that doing that would be quicker than having an array of pointers to double due to the pointer load's taking at least 2 cycles on modern chips (assuming L1 cache hit), whereas multiplication always takes 2 cycles (or 3 on Intel).

Of course it's quicker still if you can use constant dimensions by e.g. sticking them in the template parameters; there's a bunch of ways to get small static muls with lower execution latencies than a multiply instruction if the multiplier is constant. Powers of two being the canonical example.

10 Name: #!/usr/bin/anonymous : 2007-10-31 08:01 ID:Heaven

Oops, looks like a 32-bit unsigned multiply has a 3-cycle latency on Athlon 64s after all. Anyway, it's slightly slower than that on Intel.

11 Name: dmpk2k!hinhT6kz2E : 2007-10-31 15:28 ID:Heaven

>>9
Where do you get these timings? I've looked at Intel's manuals in the past and they don't seem to have them anymore.

12 Name: #!/usr/bin/anonymous : 2007-10-31 15:38 ID:tD19ZakE

13 Name: #!/usr/bin/anonymous : 2007-10-31 16:10 ID:Heaven

>>12

When you actually know this stuff, this kind of optimization is entirely free and has no side effects. Blindly following dogma that says "OH MY GOD DON'T OPTIMIZE" is just as dumb as spending lots of effort optimizing seldom-called code.

14 Name: #!/usr/bin/anonymous : 2007-11-01 01:13 ID:0NOM7tK7

>>9
Way to stroke your e-rection in public. My, what an enormous e-penis you have!

15 Name: dmpk2k!hinhT6kz2E : 2007-11-01 03:01 ID:Heaven

More content, less flaming. Thank you.

16 Name: #!/usr/bin/anonymous : 2007-11-01 13:26 ID:xnFkfGcc

>>7
x[y]->z is perfectly valid.

Assume x is an array of pointers to structs of type **x.

We have:

struct foo { int z; };
/* ... */
struct foo *x[10];
size_t y = 0;
int bar;
x[y] = malloc(sizeof **x);
if(x[y])
bar = x[y]->z = 0;
free(x[y]);

17 Name: #!/usr/bin/anonymous : 2007-11-01 13:29 ID:Heaven

>>16
disregard that, i just realized dmpk2k was talking about x[y]->[z]
Hm.. x[y]->[z] is not of the form x[y], since z[x[y]->] is not valid, x[y]->[z] is not valid either.

That's how you validate array syntax btw, simply reverse it.
x[y] == y[x] after all

18 Name: #!/usr/bin/anonymous : 2007-11-01 15:13 ID:Heaven

Confusingly, $x[$y]->[$z] is valid syntax in at least Perl, and is actually equivalent to $x[$y][$z]

19 Name: #!/usr/bin/anonymous : 2007-11-02 12:31 ID:AkEOQ1Xr

>>18
I think you mean that it's equivalent to @{$x[$y]}[$z]. -> dereferences a reference, only somewhat unlike a @/$/% prefix would. I.e. $hashref->{something} works like %$hashref{something}.

>>11
They haven't listed simple instruction timings since the ppro started with the micro-ops thing. From a P2/P3 optimization manual I downloaded some years back, it seems that a "MUL eax, m/r32" instruction is decoded to 3 dependent micro-operations. (IMUL between registers decodes to just one.)

So yeah, a lot of seat-of-the-pantsing here. The point is that multiply operations are cheaper than indirecting through a pointer array, and if you're doing FP matrix computations they'll issue to the otherwise unused integer units which makes them close as can get to free.

20 Name: #!/usr/bin/anonymous : 2007-11-02 12:31 ID:Heaven

I mean "works like $$hashref{something}" of course. Dammit.

21 Name: #!/usr/bin/anonymous : 2007-11-02 12:44 ID:Heaven

To test whether 0/1 is valid syntax I will just reverse it.

OH SHII---

22 Name: #!/usr/bin/anonymous : 2007-11-03 03:26 ID:M740Z140

>>19

No, I tested it before posting to be sure. The two expressions I posted are equivalent. Yours may be a third way of doing the exact same thing, but I haven't tested.

23 Name: #!/usr/bin/anonymous : 2007-11-03 11:22 ID:Heaven

>>21

> validate array syntax ..

24 Name: #!/usr/bin/anonymous : 2007-11-05 21:05 ID:AkEOQ1Xr

>>22
A noise operator in Perl? I think I may have to start hating it because of this. (More likely it's that [] automagically dereferences if it's a reference to an array... this kind of DWIM isn't good for readability.)

25 Name: #!/usr/bin/anonymous : 2007-11-16 02:00 ID:9zkq4T1j

As noted in http://4-ch.net/code/kareha.pl/1194968102/l50, the STL is the best for dealing with large and/or unruly dynamically allocated multidimensional arrays (vectors). I'll admit that STL enumeration is not pretty, but you can write flexible methods (or wack macros) to make it a lot less painful. More benefits of using the STL are that you can be much more eloquent with exception handling than I am below, there are much more advanced containers than simple vectors, and you can write comparator functions for very efficient and simple sorting.

I'm pretty sure the only other workable option is ** if you need to be passing the 2D (or more) arrays, which is a huge pain in the ass; but I could be wrong, I pretty much only use STL.

Here's a simple MSVC++ example. Shouldn't be that hard to port to gcc -- just delete the stdafx line and fix the type of argv in the main() parameter list and you should be all set I think:

#include "stdafx.h"
#include <vector>
typedef std::vector<int> IntegralArray;
typedef std::vector< IntegralArray > IntegralMatrix;
void IncrementDynamicMatrix(IntegralMatrix &matrix)
{
for(IntegralMatrix::iterator iter = matrix.begin();
iter != matrix.end();
iter++)
{
for(IntegralArray::iterator iter2 = (*iter).begin();
iter2 != (*iter).end();
iter2++)
{
(*iter2)++; // Increment each int.
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
// Create a 200x200 2D non-jagged array/vector.
IntegralMatrix matrix(200);
		// Populate the matrix with increasing
// ints; [0,0] = 0, [199,199] = 39,999.
int i = 0;
for(IntegralMatrix::iterator iter = matrix.begin();
iter != matrix.end();
iter++)
{
(*iter).resize(200);
for(IntegralArray::iterator iter2 = (*iter).begin();
iter2 != (*iter).end();
iter2++)
{
(*iter2) = i++;
}
}
                    // Pass the matrix by reference.
IncrementDynamicMatrix(matrix);
		// Now the matrix is:
// [0,0] = 1, [199,199] = 40,000.
// Nifty, eh?
		// Do some more stuff with the matrix.
// Print it out, whatever.
}
catch(...)
{
printf("Oops!");
return -1;
}
	return 0;
}
This thread has been closed. You cannot post in this thread any longer.