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);
b开发者_运维问答ecause 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?
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];
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.
精彩评论