Q: Please comment on the usage of structures and structure assignment operations in C
I am working on converting a MATLAB program to C using BLAS and LAPACK for linear algebra support. The MATLAB code uses cell arrays. I created a Matrix datatype and a Cell data-type.
A section of the header file/implementation:
#define ASSERT(c,m)
#define PREC double
#define ZEROS(r,c) (zeros(r,c))
#define ONES(r,c) (ones(r,c))
#define EYE(r,c) (eye(r,c))
#define ALLOCM(r,c) (alloc_matrix(r,c))
#define PRINTM(M) (print_matrix(M))
#define FREEM(M) (free_matrix(M))
#define ALLOCC(r,c) (alloc_cell(r,c))
#define GETMC(C,r,c) (get_matrix_from_cell(C,r,c))
#define SETMC(C,r,c,M) (set_matrix_in_cell(C,r,c,M))
#define FREEC(C) (free_cell(C))
/* Matrix */
typedef struct {
PREC * array;
int rows; // The number of rows in the matrix
int cols; // The number of columns in the matrix
}Matrix;
/* Cell of Matrices */
typedef struct {
Matrix * array; // Cell array of matrices stored in row major form
int rows; // Number of rows in Cell array
int cols; // Number of cols in Cell array
}Cell;
/* Matrix utility functions */
Matrix alloc_matrix(int rows, int cols);
Matrix zeros(int rows, int cols);
Matrix ones(int rows, int cols);
Matrix eye(int rows, int cols);
Matrix corrcov(Matrix matrix);
void print_matrix(Matrix matrix);
void free_matrix(Matrix matrix);
/* Cell array utility functions */
Cell alloc_cell(int rows, int cols);
INLINE Matrix get_matrix_from_cell(Cell cell, int row, int col);
INLINE void set_matrix_in_cell(Cell cell, int row, int col, Matrix matrix);
void free_cell(Cell cell);
// Implementation
Matrix
alloc_matrix(int rows, int cols){
Matrix matrix;
ASSERT(rows > 0 && cols > 0, FATAL_NEGATIVE_DIMENSIONS);
matrix.array = (PREC *) malloc(sizeof(PREC) * rows * cols);
ASSERT(matrix.array != NULL, FATAL_NO_MEMORY);
matrix.rows = rows;
matrix.cols = cols;
return matrix;
}
Matrix
zeros(int rows, int cols){
int i;
int size;
Matrix matrix;
matrix = alloc_matrix(rows, cols);
for(i = 0, size = rows * cols; i < size ; i++){
matrix.array[i] = 0.0;
}
return matrix;
}
void
print_matrix(Matrix matrix){
int i;
int j;
int k;
int rows = matrix.rows;
int cols = matrix.cols;
ASSERT(rows > 0 && cols > 0, FATAL_NEGATIVE_DIMENSIONS);
ASSERT(matrix.array != NULL, FATAL_NULL_POINTER);
printf("\n Rows: %d, Cols: %d\n", rows, cols);
for(i = 0 ; i < rows; i++){
for(j = 0, k = i * cols; j < cols; j++){
printf("%8.6f ", matrix.array[ k + j ]);
}
printf("\n");
}
}
void
free_matrix(Matrix matrix){
ASSERT(matrix.array != NULL, FATAL_NULL_POINTER);
free(matrix.array);
matrix.array = NULL;
}
Cell
alloc_cell(int rows, int cols){
Cell cell;
ASSERT(rows > 0 && cols > 0, FATAL_NEGATIVE_DIMENSIONS);
cell.array = (Matrix *) malloc(sizeof(Matrix) * rows * cols);
ASSERT(cell.array != NULL, FATAL_NO_MEMORY);
cell.rows = rows;
cell.cols = cols;
return cell;
}
void
free_cell(Cell cell){
int i;
int size;
int rows = cell.rows;
int cols = cell.cols;
ASSERT(rows > 0 && cols > 0, FATAL_NEGATIVE_DIMENSIONS);
ASSERT(cell.array != NULL, FATAL_NULL_POINTER);
for( i = 0, size = rows * cols; i < size; i++){
free_matrix(cell.array[i]);
}
free(cell.array);
}
INLINE Matrix
get_matrix_from_cell(Cell cell, int row, int col){
Matrix matrix;
int rows = cell.rows;
int cols = cell.cols;
ASSERT(cell.array != NULL, FATAL_NULL_POINTER);
ASSERT(rows > 0 && cols > 0, FATAL_NEGATIVE_DIMENSIONS);
ASSERT(row >= 0 && row < rows, FATAL_INDEX_OUT_OF_BOUNDS);
ASSERT(col >= 0 && col < cols, FATAL_INDEX_OUT_OF_BOUNDS);
matrix = cell.array[(row * cols) + col];
return matrix;
}
INLINE void
set_matrix_in_cell(Cell cell, int row, int col, Matrix matrix){
int rows = cell.rows;
int cols = cell.cols;
ASSERT(cell.array != NULL, FATAL_NULL_POINTER);
ASSERT(rows > 0 && cols > 0, FATAL_NEGATIVE_DIMENSIONS);
ASSERT(row >= 0 && row < rows, FATAL_INDEX_OUT_OF_BOUNDS);
ASSERT(col >= 0 && col < cols, FATAL_INDEX_OUT_OF_BOUNDS);
cell.array[(row * cols) + col] = matrix;
}
Notice that I do not return pointers from functions that allocate Matrix type objects or Cell type objects. I return a plain Matrix or Cell object, but I allocate the arrays (PREC type for matrices and Matrix type for cells - where PREC is double or float) inside these functions.
This is convenient because:
struct assignment is a valid operation in C (I know that the dynamically allocated data is not duplicated, only a reference to it)
to get to an element inside a Matrix, I can use the
dot
syntax instead of->
syntax (also I believe I may have to de-reference just once if I use the dot syntax - matrix.array[i] vs matrix->array[i] (I mean the matrix in matrix->array[i] is a pointer to a Matrix object, as I am comparing items on a stack vs those that are dynamically allocated)Most of the Matrix objects and Cell objects ( barring the dynamically allocated memory ) are on the stack and are automatically freed when function exits and I feel, its easier to keep track of objects that are no longer in use and free them when necessary.
I profiled a sample program (that uses this interface) with Valgrind.
The sample program:
#include "matutil.h"
int main(){
Matrix mz;
Cell cell;
mz = ZEROS(3,3);
PRINTM(mz);
cell = ALLOCC(1,1);
SETMC(cell,0,0,mz);
PRINTM(GETMC(cell, 0 , 0));
FREEC(cell);
return 0;
}
The problem is matrix object (Matrix mz) that I start with does not have its array field explicitly initialized and so, Valgrind reports
==17433== Conditional jump or move depends on uninitialised value(s)
Is this an issue? Are there any pitfalls that I should be aware of before I proceed with this design?
Thank you.