2

I am a complete noob at C (<1 week) and I'm trying to get a grasp how to work on it, although I'm familiar with programming in other languages. As a first goal, I wanted to write a function to do Gauss reduction on a matrix. I have no problem with the algorithm, but it turned out I do not know how to represent a matrix. For simplicity, let me assume that we work with float entries.

The first naif way would be to use an array of arrays like

float naifMatrix[3][3] = {
    {2, 1, 3},
    {0, -1, 4},
    {1, 3, 0}
};

The problem is that you cannot pass such an object as an argument without knowing the dimensions a priori (of course I want to be able to use matrices of arbitrary size, which is not known at compilation time). One does not see this problem when working with vectors and representing them as arrays. If I do

float vector[3] = {1, 2, 3};
norm(vector);

it will work, provided I declare norm like

norm(float * vector);

When vector is passed, it is converted to &vector[0], and not much information is lost (basically one has to keep track of the length). But I cannot just call

gaussReduction(naifMatrix);

and declare gaussReduction with

gaussReduction(float ** naifMatrix);

because naifMatrix is converted (and rightly so) to a pointer to an array of floats, not in a pointer to a pointer. Since I do not know how big this array will be, I do not see a way to declare gaussReduction.

Of course I could cheat by passing a pointer to a void, but before dereferencing it, I would need to cast it to the right type (float[3] *), which, again, I do not know a priori. Moreover it seems to me that by abusing of void * one defeats one of the purposes of using C over other languages, which is a strict type checking.

The best solution I have found so far is to use a struct. A matrix is basically given by the list of its entries and the two dimensions. So I can do

struct matrix {
    float * begin;
    int rows, columns;
};

and use it as

struct matrix matrix = {&naifMatrix[0], 3, 3};

The problem is that this is still annoying. First it is akward to get a struct matrix from a double array, and second one has to give the dimensions explicitly. I would be happy with wrapping this with a sort of "constructor" function, like

struct matrix matrix = Matrix(naifMatrix);

but I cannot do this for two reasons. First, I have the same problem as above in passing naifMatrix as argument to a function. Second, even if I could pass it, I would get a pointer, and thus I would not be able to get information on the dimensions (in this case, that both are 3).

Is there a more sensible way to pass around and manipulate the datum of a matrix?

Andrea
  • 20,253
  • 23
  • 114
  • 183
  • I suggest you buy a copy of *Numerical Recipes in C* and read that. You'll learn all you need to there, and more. – David Heffernan Jan 18 '11 at 13:16
  • http://stackoverflow.com/questions/4051/passing-multidimensional-arrays-as-function-arguments-in-c related. – Rozuur Jan 18 '11 at 13:21
  • @David: thank you, but this is not just what I want to do. :-) I'm just playing to learn C, I'm not looking forward to build a linear algebra library. – Andrea Jan 18 '11 at 13:39
  • http://stackoverflow.com/questions/16004668/c-allocating-a-matrix-in-a-function/27366086#27366086 Above you will find a program that I have made with functions allocating and manipulating matrices in any possible way for C (gcc C11/C99). Maybe it will be useful 4u... – 42n4 Dec 08 '14 at 20:23

2 Answers2

6

C99 added variable-length arrays to the language:

_Bool gaussReduction(size_t rows, size_t cols, float matrix[rows][cols]);

If you have the definition

float naifMatrix[3][3] = {
    {2, 1, 3},
    {0, -1, 4},
    {1, 3, 0}
};

you can get at the dimensions via

size_t rows = sizeof naifMatrix / sizeof *naifMatrix;
size_t cols = sizeof *naifMatrix / sizeof **naifMatrix;

You can use macros to minimize repetition. Using

#define rowsof(MATRIX) (sizeof (MATRIX) / sizeof *(MATRIX))
#define colsof(MATRIX) (sizeof *(MATRIX) / sizeof **(MATRIX))
#define matrixarg(MATRIX) rowsof(MATRIX), colsof(MATRIX), (MATRIX)

you'd end up with

gaussReduction(matrixarg(naifMatrix));

or, using a compound literal instead of a variable, with

gaussReduction(matrixarg(((float [3][3]){ 
    {2, 1, 3},
    {0, -1, 4},
    {1, 3, 0}
})));

Using a variable-length array has the same performance characteristics as the equivalent C90 code - the only thing you'll gain is nicer syntax:

// C99:
_Bool gaussReduction(size_t rows, size_t cols, float matrix[rows][cols])
{
    // size_t i = ..., j = ...
    float x = matrix[i][j];

/* C90: */
int gaussReduction(size_t rows, size_t cols, float *matrix)
{
    /* size_t i = ..., j = ... */
    float x = matrix[i * cols + j];
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • Thank you very much! I guess this is what one gets by studying C from a book on the previous version of he standard... :-D By the way, is there a performance loss when using variable sized array with respect to fixed size? – Andrea Jan 18 '11 at 13:28
  • @Andrea: variable-length arrays only carry a performance-penalty insofar as the array size isn't known at compile-time - however, this information is lost when passing an array as function argument anyway; I'll add some details about the C90 equivalent to my answer... – Christoph Jan 18 '11 at 13:36
-1

If you define thus:

float naifMatrix[][] = {
    {2, 1, 3},
    {0, -1, 4},
    {1, 3, 0}
};

You should have a pointer to an array of pointers. Then you can use

gaussReduction(float ** naifMatrix);

My C is rusty, though.

Satya
  • 4,458
  • 21
  • 29
  • C arrays are not jagged - you can't convert a two-dimensional array to a poiter-to-pointer – Christoph Jan 18 '11 at 13:06
  • You can't declare `naifMatrix[][]` such an array. Only first array subscript is optional. It should have been `naifMatrix[][3]`. – Mahesh Jan 18 '11 at 13:12
  • This is exactly my problem. A two dimensional array will be at most converted in a pointer to an array. – Andrea Jan 18 '11 at 13:12
  • Actually there is nothing wrong in that: a pointer to a pointer would a completely different type of data. But the fact remains that I would like to be able to get at least a pointer to the beginning of the chunk of data, which I do not know how to do. – Andrea Jan 18 '11 at 13:15
  • Don't declare/define it statically, then. gah, I can't insert newlines in my comment? Sheesh. Ah, I can copy-paste them. float **naifMatrix; float *naifMatrix1 = {2,1,3}; naifMatrix = malloc(3 * sizeof(float *)); naifMatrix[0] = naifMatrix1; something like that, maybe? Like I said, my C's rusty. – Satya Jan 18 '11 at 16:20