Array Flipping


On October 21, 2000, Elrac said:
// Flip an array of integers across its diagonal, in place.
// This assumes that 'mat' is a pointer to the first element in the array
// and that the array is square, with 'size' rows and columns.
// Note that the array in the calling function is 2 dimensional; we pass
// it as a single *int so we don't have to declare its type in the argument
// list. This means we have to calculate all locations ourselves, though.
//
// example call:
//
// int BigMatrix[10][10];
// ... // coding to fill BigMatrix
// flip(&BigMatrix[0][0], 10);
//
void flip(int *mat, int size) {
  int temp, r, c;
  for (r=0; r<size; r++) {
    for (c=0; c<size-r; c++) {
      if (r + c + 1 == size) continue; // on diagonal, don't bother
      temp = mat[r * size + c];
      mat[r * size + c] = mat[c * size + r];
      mat[c * size + r] = temp;
    }
  }
}
        
On October 22, 2000, Elrac said:
I tried to explain the bit about treating the two dimensional array as a 
one dimensional array in the comment above my code. Here is a more thorough 
explanation:

Yes, the original matrix is 2 dimensional. The problem is that, in order to 
figure out where the elements of an array are, the compiler needs to be told 
about the size of all but the first dimension. In this case, you could declare 

   (1) void flip(int mat[10][10], int size)

and you could also declare

   (2) void flip(int mat[][10], int size)

but it wouldn't work with

   (3) void flip(int mat[][], int size)

because the compiler needs to know how many elements are in a row in order to 
locate elements in the nth row.

You don't see this problem with 1 dimensional arrays because it's OK to leave 
out the dimension. A 1 dimensional array is just a bunch of items strung out 
in a long line, and giving the size doesn't tell the compiler anything it 
really needs.

As I said, (1) and (2) would work fine but then the function would only work 
for matrices of size 10, and we always try to make our routines general 
enough so that they can be used for different data.

My solution, above, is based on the fact that we know how data is laid out in 
memory. Given an array like this:

   a b c
   d e f
   g h i

we know that in memory, it's really just a one dimensional list like this:

   a b c d e f g h i

so instead of passing in a 2 dimensional array, I took the address of (a), 
told the compiler I was passing in a pointer to 1 or more integers, then I 
used this pointer like a 1 dimensional array, and did all the math myself. 
If the pointer is called mat, then mat[0] is the same as *mat, and then 'a' 
is mat[0], 'b' is mat[1] and so forth. How about 'g'? In the original matrix, 
it's at [2][0]; you need to skip 2 rows to get to it. Since we know each row 
has 3 columns, we know it's in mat[6]. Similarly, to get to 'h', you skip 
down 2 rows (that's 6) and 1 column (add 1 more) so you end up in mat[7].

Generalizing this, assuming the original array, call it 'matrix,' is square 
(and it needs to be, for a diagonal to be meaningful) and both of its 
dimensions are 'size', then matrix[row][col] is (row*size + col) away from 
the beginning of the array, and this is exactly the subscript to use if the 
array is 1 dimensional.

So what I did was treat the 2 dimensional array as a 1 dimensional array 
and took it upon myself to do the math that the compiler does if it knows 
the dimensions of the 2 dimensional array.

This is one of the bits of trickery that C allows you to do by being very 
free with interconversions of arrays and pointers. It also works in older 
versions of FORTRAN and (probably, for upward compatibility) C++, but not 
in most other programming languages.

For those of you who feel really weird doing this kind of thing, I'm happy 
to report that there's a less tricky solution available, which I'll present 
in my next message.
        
On October 22, 2000, Elrac said:
Since we know the dimensions of the array are 'size', and 'size' is passed in 
as one argument, we could try this:

   void flip(int mat[size][size], int size)

but at least the compiler used for Astonia (gcc) gets really upset about that. 
You're using a name to dimension the array that it's never heard about.

On the other hand, if you code it so it knows 'size' before using it, it works 
like a charm:

   void flip(int size, int mat[size][size])

Again, the first dimension is irrelevant to the compiler, so hardcore C 
programmers will leave it out:

   void flip(int size, int mat[][size])

and this would be the solution based on passing a 2 dimensional array:

   void flip(int size, int mat[][size]) {
     int temp, r, c;
     for (r=0; r<size; r++) {
       for (c=0; c<size-r; c++) {
         // I think the if() test wasn't needed after all
         temp = mat[r][c];
         mat[r][c] = mat[c][r];
         mat[c][r] = temp;
       }
     }
   }
Back to Table Of Contents