1

I've been doing some matrix calculation in C for university the other day where I had a 5x5 matrix to begin with so I hard-coded it into the source. It was a 2D array of doubles like:

/**
 * This is the probability-matrix for reaching from any profile
 * to another by randomly selecting a friend from the friendlist.
 */
static const double F[5][5] = {
  /*           P    , F    , L    , A    , S    */
  /* Peter */ {0    , 0.5  , 0.5  , 0    , 0    },
  /* Franz */ {1.0  , 0    , 0    , 0    , 0    },
  /* Lisa  */ {0    , 1/3.0, 0    , 1/3.0, 1/3.0},
  /* Anna  */ {0    , 0    , 0    , 0    , 1    },
  /* Sepp  */ {0    , 0.5  , 0.5  , 0    , 0    }
};

I wanted my functions to not be fixed to operate on 5-by-5 matrices so I always pass the number of rows and/or cols to the function. This forces me not to use the double [][X] syntax because it can not be completely "variable" and instead use a double* as function parameter.

inline size_t matrix_get(int rows, int i, int j);

inline void matrix_print(double* m, int rows, int cols);

inline void matrix_copy(double* d, const double* s, int rows, int cols);

void matrix_multiply(
  int m, int n, int l,
  const double* a, const double* b, double* d);

But I always get this warning when calling a function that accepts double* when I passed double [5][5] instead.

fuenf_freunde.c:138:17: warning: incompatible pointer types passing 'double [5][5]' to parameter of
      type 'double *' [-Wincompatible-pointer-types]
  matrix_print( R, 5, 5);
                ^
fuenf_freunde.c:54:27: note: passing argument to parameter 'm' here
void matrix_print(double* m, int rows, int cols)
                          ^

Casting using (double*) F resolves the warning.

Now my questions are

  • am I wrong casting a 2D double array to a double pointer?
  • why is it working if its illegal?
  • what is the right way to pass an n-dimensional arbitrary size array to a function?

EDIT: This cleared a lot up for me: Accesing a 2D array using a single pointer

So I should just use double[x*y] instead of double[x][y] I think. But is it legal to cast double[] to double*?

Community
  • 1
  • 1
Niklas R
  • 16,299
  • 28
  • 108
  • 203
  • 2
    What would make you think that `double[5][5]` and `double*` would be compatible? If anything the common mistake is to think that an array or arrays are equal to a pointer to pointer ([but they're not](http://stackoverflow.com/a/18440456/440558)). The type `double[5][5]` is equivalent to `double(*)[]`. – Some programmer dude Apr 16 '15 at 05:40
  • 1
    @JoachimPileborg Just like `double[5]` is compatible with `double*` I guess... At least, clang doesn't complain about this. And since `double[5][5]` is layed out in memory as `double[25]` – Niklas R Apr 16 '15 at 05:43
  • Ask yourself; is a pointer to a `double` the same as a pointer to a `double[N]`? Also, this sort of mindset will get you in trouble in C: *(since my code works as expected) why is the compiler complaining?* – Ed S. Apr 16 '15 at 05:43
  • @EdS. Assuming I would be casting the first block of memory that matches the size of a double pointer from the double array, wouldn't it be highly unlikely that its value is the same as the actual memory location of the array? | I thought I had read somewhere that a 2D array is just a 1D array in C/C++, indices automatically converted to a linear index by the compiler. – Niklas R Apr 16 '15 at 05:46
  • @NiklasR C has a type system. `int` is laid out in memory the same as `char[4]` too – M.M Apr 16 '15 at 06:34
  • @McNabb Wait, now what is it? @EdS said that `double[][]` is actually a pointer to `double[]`, now when I understand you right you say that `double[x][y]` again is equal to `double[x*y]` (which is what I thought). – Niklas R Apr 16 '15 at 12:46
  • @McNabb I found a link with a very good explanation, what EdS said makes sense to me know, but I'm still not sure what you wanted to say specifically. – Niklas R Apr 16 '15 at 12:55
  • "why is it working if its illegal?" – undefined behavior is… well… **undefined.** it's not called "guaranteed crash" or "guaranteed exception" *for a reason.* – The Paramagnetic Croissant Apr 16 '15 at 13:00
  • 1
    @ThePramagneticCroissant Thank you, that much I understand. But what makes it work in this specific case? If `double[][X]` is the same as `(double*)[X]`, how could `(double*) data` ever ever ever have the same value as `&data[0][0]`? (in terms of the memory location they point to) – Niklas R Apr 16 '15 at 13:13

1 Answers1

2

I contend that there is nothing wrong with what you do. Your code just lacks an explicit cast which would get rid of the warning, indicating that your assignment is intentional.

You can access a one dimensional array through a pointer to its elements, and recursively applied, it follows that n-dimensional matrices can be accessed through pointers to the atomic elements.

C guarantees a contiguous memory layout for them. This pattern is common. In fact passing arrays is hard or impossible to do differently, given the little information C stores with types (as opposed to, say, C#). So the matrix decays to a pointer to its first element (in your case to the first one dimensional array), which can safely be cast to an address of its first element, a double. They all share the same numerical address.

Typical concerns with pointer type casting are aliasing issues with modern optimizing compilers (a compiler can assume that you don't access memory through pointers of unrelated types except char*), but there is an explicit exemption in the standard draft 1570 which I have, par. 6.5.7, which I think is applicable here:

An object [that would be a double in your matrix, -ps] shall have its stored value accessed only by an lvalue expression that has one of the following types:

— a type compatible with the effective type of the object [that would be a dereferenced double pointer, -ps] [...]

— an aggregate [e.g. array, -ps] [...] type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union) [that would be your matrix variable, containing, eventually, doubles, -ps]

[...]

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62