Thread: Modifying an array from within a function

1. Modifying an array from within a function

Hello, this is my first post. I'm fairly new to programming and i have some uncertainty regarding the manipulation of arrays through a function that they are passed to.

The context is a matrix transformation, where i want to call a function with an array m[4][4] and a few transformation parameters as its arguments.

Code:
```void matrixTransform(double *m, double rx, double ry, double rz, double tx, double ty, double tz)
{
/* Here i do a few mathematical calculations.
Essentially, i want to set m = m * mt, where mt is an array[4][4]
that is built from the (rx, ry, ...) parameters on each function
call.
*/
}```
I have browsed related questions here on the forum and elsewhere on the web, but they don't address my confusion, so i though i'd go through it step by step to finally get clarity.

My question is regarding the proper syntax for handling arrays passed to functions. (1) Do i understand it correctly, that an asterisk needs to be placed before first parameter m, because the function is to expect an array (pointer)?

(2) And are these two lines equivalent in meaning/behavior?
Code:
```void matrixTransform(double *m) {...}
void matrixTransform(double m[]) {...}```
(3) I have also seen the use of
Code:
`function(double **var)`
in relation to array handling, which confused me even more.

(4) Since my code does an (abstractly described) m = m * mt assignment, i suspect i need to create a copy of m within the function, so that i can read from it's original contents, while writing new ones to it.

e.g.
Code:
```void matrixTransform(double *m)
{
/* (again, pseudo-code because it's redundant to type it all out)
these are 2D arrays of size [4][4]. */
m2 = m;
m = m2 * mt;
}```
(5) What would be a robust but fast method to create this copy? A simple declaration of m2 followed by a for-loop to transfer m's contents to it?

Regarding array manipulation, if i write this code:
Code:
```void matrixTransform(double *m)
{
m[2][3] = 5;
}```
(6) Have i effectively changed a value in m's original address space? I imagine this would be most desirable for an array that iteratively gets its values modified, but where size (16 x 8 bytes) stays constant.

(7) Also, am i right in thinking the function can be of type void, as i'm not returning a value but manipulating a pre-existing array?

Sorry for all the questions. Not 'getting it', i simply don't want to use trial and error in C; it's too easy to mess up.

2. 2. Yes

3. Well, pointers can emulate arrays. So var was set up to point to some pointers, which pointed to the actual memory being used as an array.

4. From what I remember on matrix multiplication, you will need a separate matrix for the answer and not necessarily copies of the operands, because the operands stay the same but the answer has a different shape.

5. Unless I'm not getting something, this is irrelevant.

6.  Yes you changed a value in m. Specifically the third value down, and fourth across. I misread the question. [/edit]

7. Yes.

What actually happens when you pass in a multidimensional array to a function is that the first dimension becomes a pointer to the other dimensions. The other dimensions will stay array type. They need to have their dimension sizes specified or there will be a compiler error.

There can be subtle differences between types:
Code:
```void matTransform (double (*m)[5] ); /* OK */
void matTransform2(double m[][5] );  /* OK */
void matTransform3(double *m[5] );   /* wrong - m is an array of 5 pointers to double */
void matTransform4(double **m );     /* dimensions aren't specified; can only work for variables starting out as double **m */
void matTransform5(double m[][] );   /* error: array type has incomplete element type */```

3. 6. won't compile. vc2012 gives the error "error C2109: subscript requires array or pointer type". you passed in a pointer, which resolves to a single indexed array. the compiler doesn't know the extents of the columns so it can't compile it.

if your function needs to operate on a 2d array, you can use whiteflags declaration "void matTransform2(double m[][5] );" if the coumn extent is known at compile time, or you can pass in the bare pointer "double *m" and compute the array index yourself. for a 2d array, it would be "index = row * column_length + col. you would need to pass in or otherwise know at least the column extent to make the computation, which is what the compiler does with the "double m[][5]" declaration. If the row and column extents are not fixed at compile time, I usually pass in the row and column extents and do the computation of the index as shown.

when i have uncertainty about pointers, I draw a diagram of the variables involved and what is pointing to what.

4. Multidimensional arrays don't work well in C.

The way that an experienced C programmer would handle your problem is like this.

Code:
```typedef struct
{
double mtx[4][4];
} MATRIX;

/*
this function translates a 3D matrix by x,y and z, passed
in the second parameter.
(note you might want the last row instead of the last column if
your system is set up the other way round).
*/
void matrixTransform(MATRIX *m, double *translation)
{
m->mtx[0][3] += translation[0];
m->mtx[1][3] += translation[1];
m->mtx[2][3] += translation[2];
}```

Malcolm: What would you say is the problem with multidimensional arrays? Does the struct solve help this, or is it just for readability? I could use an array[16] instead of [4][4] but i prefer the latter for the sake of readability (it's easier to think in rows/columns).

dmh2000: I have now changed the function parameter to m[4][4] instead of *m, as i know the size in advance. Thanks.

What would you say is the problem with multidimensional arrays? Does the struct solve help this, or is it just for readability?
Heh, the 2D array is still there, so it is not that "multidimensional arrays don't work well in C". The advantage here is abstraction: instead of thinking in terms of and passing around a 2D array (which really decays to a pointer to its first element), you now think in terms of a matrix and pass around a matrix (using a pointer to avoid unnecessary copying).

With this abstraction, at some later point you may choose to have matrices of flexible size, e.g., by using pointers to dynamically allocated memory and storing the dimensions in the matrix object.

7. Thanks, it works!

It was a success, my first real program works as intended and is about 20 times faster than my Python/Numpy implementation.
I familiarized myself with structs as you suggested, and it has really helped me. I think i will be moving to C++, as the object oriented approach makes working with these things a lot easier to keep track of.

On the off-chance that someone searches the forum for a similar question, specifically matrix transformations, i want to mention the following findings:

• Reducing the calculations for affine transformations, by leaving out permanently untouched "0"s of the matrix, actually hurts performance. Puzzling. maybe the non-sequential readout is harder to optimize for the the compiler.
• Edit: Using array[16] instead of array[4][4] is negligible to performance.