C -- changing a multi-dimensional array to CONST in a function? - c

I have the following c function declaration:
float Sum2d( const unsigned int nRows, const unsigned int mCols, float arr[nRows][mCols] )
{
float sumAll = 0;
// I would like to make this change illegal!
arr[0][0] = 15;
for (int i = 0; i < nRows; i++)
for (int j = 0; j < mCols; j++)
sumAll += arr[i][j];
return sumAll;
}
Using the code:
int main()
{
// define a 2d float array
float myArr2d[3][2] = {{1,2}, {3,4}, {5,6}};
// calculate the sum
float sum = Sum2d(3, 2, myArr2d);
// print the sum
printf("%f\n", myOpResult);
// return 1
return 1;
}
This function works well, yet there's one problem: the elements of arr can be altered in the Sum2d() function.
How can I change Sum2d()'s prototype to prevent any changes to arr's elements?

Multidimensional arrays with const qualification are difficult to handle. Basically you have the choice to cast non-const arrays at every call side, to avoid such const arrays as arguments completely, or to deviate by using some sophisticated macros. This is a longer story, you may read it up here.

I don't know what compiler you're using, but that doesn't compile for me as C or C++.
But regardless, just making arr const should suffice.

Change the prototype of the function to use const with float
Also you have specified nRows / nCols in array argument, which is not allowed in C. If you don't know the bounds of array, use double pointer.
This approach doesn't prevents typecasting in the function.
#include <stdio.h>
float Sum2d( const unsigned int nRows, const unsigned int mCols, const float arr[][2] )
{
float sumAll = 0;
// I would like to make this change illegal!
//arr[0][0] = 15;
for (int i = 0; i < nRows; i++)
for (int j = 0; j < mCols; j++)
sumAll += arr[i][j];
return sumAll;
}
int main()
{
// define a 2d float array
float myArr2d[3][2] = {{1,2}, {3,4}, {5,6}};
// calculate the sum
float sum = Sum2d(3, 2, (const float (*)[2])myArr2d);
// print the sum
printf("%f\n", sum);
// return 1
return 1;
}
Since you are using following command line i suppose:
gcc <file.c> -o out -std=c99
Running on Debian Squeeze
$ gcc array.c -o array -std=c99
$ gcc --version
gcc (Debian 4.4.5-8) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Related

2-dimensional array in a struct in C

I'm trying to initialize a 2-dimensional array in a structure but I always get an error :
gcc -g -Wall -W -I/usr/include/SDL -c -o fractal.o fractal.c
In file included from fractal.c:2:0:
fractal.h:12:12: error: array type has incomplete element type ‘double[]’
double values[][];
Here's the code:
struct fractal {
char name[64];
int height;
int width;
double a;
double b;
double meanValue;
double values[][]; /*This line is causing the error*/
};
Ideally I'd like to initialize the height and width of the 2-dimensional array like this:
struct fractal {
/*... Same code as above ...*/
double values[width][height];
};
But then I get two other errors when compiling:
gcc -g -Wall -W -I/usr/include/SDL -c -o fractal.o fractal.c
In file included from fractal.c:2:0:
fractal.h:12:19: error: ‘width’ undeclared here (not in a function)
double values[width][height];
^
fractal.h:12:26: error: ‘height’ undeclared here (not in a function)
double values[width][height];
^
I've looked about everywhere but my code should work and I can't figure out why it doesn't.
Thanks for the help
As a disclaimer, this is something of an advanced topic, so if you are a beginner you might want to just back away from it entirely and just use a double* array followed by a call to malloc for each pointer. (Fine for beginners, unacceptable in professional code.)
It is an advanced topic since this particular case is a weakness in C. The feature you are trying to use, with an empty array at the end of a struct, is known as flexible array member. This only works for one dimension however. If both dimensions are unknown at compile time, you have to come up with a work-around.
The allocation part is as for any flexible array member: allocate the struct dynamically and make size for the trailing array.
fractal_t* f = malloc(sizeof *f + sizeof(double[height][width]) );
(In this case taking advantage of the convenient VLA syntax, although a flexible array member is not a VLA.)
Technically, the last member of the struct is supposedly double[] now, or so says the struct declaration. But memory returned by malloc has no actual effective type until you access it, after which the effective type of that memory becomes the type used for the access.
We can use this rule to access that memory as if it was a double[][], even though the pointer type in the struct is a different one. Given a fractal f, the code for accessing through a pointer becomes something like this:
double (*array_2D)[width] = (double(*)[width]) f->values;
Where array_2D is an array pointer. The most correct type to use here would have been an array pointer to an array of double, double (*)[height][width], but that one comes with mandatory ugly accessing (*array_2D)[i][j]. To avoid such ugliness, a common trick is to leave out the left-most dimension in the array pointer declaration, then we can access it as array_2D[i][j] which looks far prettier.
Example code:
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
char name[64];
size_t height;
size_t width;
double a;
double b;
double meanValue;
double values[];
} fractal_t;
fractal_t* fractal_create (size_t height, size_t width)
{
// using calloc since it conveniently fills everything with zeroes
fractal_t* f = calloc(1, sizeof *f + sizeof(double[height][width]) );
f->height = height;
f->width = width;
// ...
return f;
}
void fractal_destroy (fractal_t* f)
{
free(f);
}
void fractal_fill (fractal_t* f)
{
double (*array_2D)[f->width] = (double(*)[f->width]) f->values;
for(size_t height=0; height < f->height; height++)
{
for(size_t width=0; width < f->width; width++)
{
array_2D[height][width] = (double)width; // whatever value that makes sense
}
}
}
void fractal_print (const fractal_t* f)
{
double (*array_2D)[f->width] = (double(*)[f->width]) f->values;
for(size_t height=0; height < f->height; height++)
{
for(size_t width=0; width < f->width; width++)
{
printf("%.5f ", array_2D[height][width]);
}
printf("\n");
}
}
int main (void)
{
int h = 3;
int w = 4;
fractal_t* fractal = fractal_create(h, w);
fractal_fill(fractal); // fill with some garbage value
fractal_print(fractal);
fractal_destroy(fractal);
}
Dynamic dimensions arrays is not the point where C is at its best... Simple Variable Length Arrays were only introduced in the language in the C99 version and were made optional in C11 version. They are still not accepted in MSVC 2017...
But here, you are trying to set one in a struct. That is not supported at all because a struct must have a constant size(*) (how could be handled arrays of structs). So I am sorry but this code should not work and I know no way to express that in C language.
A common way would be to replace the 2D dynamic array with a pointer, allocate the pointer to a 2D array and then use it, but even this is not really simple.
You have to design your struct differently...
(*) The last element of a struct may be of an incomplete type, for example int tab[];. That is a dangerous feature because the programmer is responsable for providing room for it. But anyway you cannot build an array of incomplete types.
I encountered this problem while designing a struct to hold both the domain values (N x 1 vector) and the solution values (N x M matrix) in my ODE solver, so as to simplify the function interfaces. N and M are simulation-dependent and hence are unknown a priori. I solved it by using GNU Scientific Library's vector-matrix module. I found it more streamlined to work with than casting a FAM (albeit allocated as 2D) to a standalone whole-array-pointer.
After allocating memory for the struct, all we need to do is invoke gsl_matrix_alloc() to reserve space for the matrix. After we are done, calling gsl_matrix_free() will destroy it. Please note that these functions are data-type dependent as explained in the documentation.
Filename: struct_mat.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_statistics.h>
typedef struct _fractal {
char name[64];
size_t height;
size_t width;
double a;
double b;
double meanValue;
gsl_matrix *values;
} fractal;
fractal * fractal_create(size_t height, size_t width) {
fractal * fractalObj = (fractal *) malloc(sizeof(fractal));
fractalObj -> values = gsl_matrix_alloc(height, width);
if (fractalObj == NULL || fractalObj -> values == NULL) {
fprintf(stderr, "NULL pointer returned while allocating fractal object.. Exiting program.\n");
exit(EXIT_FAILURE);
}
fractalObj -> height = height;
fractalObj -> width = width;
fractalObj -> meanValue = 0.0;
return fractalObj;
}
void fractal_populate(fractal * fractalObj) {
srand(time(NULL));
double current_value = 0.0;
for (size_t r = 0; r < fractalObj -> height; ++r) {
for (size_t c = 0; c < fractalObj -> width; ++c) {
current_value = (double) rand() / (double) RAND_MAX;
gsl_matrix_set(fractalObj -> values, r, c, current_value);
}
}
}
void fractal_calcMeanValue(fractal * fractalObj) {
gsl_vector_view colVec;
for (size_t col = 0; col < fractalObj -> values -> size2; ++col) {
colVec = gsl_matrix_column(fractalObj -> values, col);
fractalObj -> meanValue += gsl_stats_mean(colVec.vector.data, colVec.vector.stride, colVec.vector.size);
}
fractalObj -> meanValue /= fractalObj -> values -> size2;
printf("\nThe mean value of the entire matrix is %lf\n", fractalObj -> meanValue);
}
void fractal_display(fractal * fractalObj) {
printf("\n");
for (size_t r = 0; r < fractalObj -> height; ++r) {
for (size_t c = 0; c < fractalObj -> width; ++c) {
printf("%lf ", gsl_matrix_get(fractalObj -> values, r, c));
}
printf("\n");
}
}
void fractal_delete(fractal * fractalObj) {
gsl_matrix_free(fractalObj -> values);
free(fractalObj);
}
int main(int argc, char const *argv[]){
// Program takes number of rows and columns as command line parameters
switch(argc) {
case 3:
printf("Running program..\n"); // to avoid the declaration-succeeding-label error
size_t height = atoi(argv[1]);
size_t width = atoi(argv[2]);
fractal * myFractal = fractal_create(height, width);
fractal_populate(myFractal);
fractal_display(myFractal);
fractal_calcMeanValue(myFractal);
fractal_delete(myFractal);
return 0;
default:
fprintf(stderr, "USAGE: struct_mat <rows> <columns>\n");
return 1;
}
}
Compile by linking with the GSL and GSL CBLAS libraries:
gcc -std=c99 struct_mat.c -o struct_mat -lgsl -lgslcblas -lm
You may install GSL via your distribution's package manager, Cygwin on Windows or by compiling the source.
In my limited experience, using a standard data structure proves to be far easier than wrestling with either FAMs or array-of-pointers-to-1D-arrays. However, the caveat is that we have to remember allocating memory for the matrix after allocating the struct itself.

How to operate matrices of different size with one function in C?

I have a code from Mathlab, where all matrix operations are done by a couple of symbols. By translating it into C I faced a problem that for every size of matrix I have to create a special function. It's a big code, i will not place it all here but will try to explain how it works.
I also have a big loop where a lot of matrix operations are going on. Functions which are operating with matrices should take matrices as income and store results in temporary matrices for upcoming operations. In fact i know the size of matrices but i also want to make the functions as universal as possible. In oder to reduce code size and save my time.
For example, matrix transposition operation of 2x4 and 4x4 matrices:
void A_matrix_transposition (float transposed_matrix[4][2], float matrix[2][4], int rows_in_matrix, int columnes_in_matrix);
void B_matrix_transposition (float transposed_matrix[4][4], float matrix[4][4], int rows_in_matrix, int columnes_in_matrix);
int main() {
float transposed_matrix_A[4][2]; //temporary matrices
float transposed_matrix_B[4][4];
float input_matrix_A[2][4], input_matrix_B[4][4]; //input matrices with numbers
A_matrix_transposition (transposed_matrix_A, input_matrix_A, 2, 4);
B_matrix_transposition (transposed_matrix_B, input_matrix_B, 4, 4);
// after calling the functions i want to use temporary matrices again. How do I pass them to other functions if i dont know their size, in general?
}
void A_matrix_transposition (float transposed_matrix[4][2], float matrix[2][4], int rows_in_matrix, int columnes_in_matrix)
{ static int i,j;
for(i = 0; i < rows_in_matrix; ++i) {
for(j = 0; j < columnes_in_matrix; ++j)
{ transposed_matrix[j][i] = matrix[i][j];
}
}
}
void B_matrix_transposition (float transposed_matrix[4][4], float matrix[4][4], int rows_in_matrix, int columnes_in_matrix)
{ static int i,j;
for(i = 0; i < rows_in_matrix; ++i) {
for(j = 0; j < columnes_in_matrix; ++j)
{ transposed_matrix[j][i] = matrix[i][j];
}
}
}
The operation is simple, but the code is massive already because of 2 different functions, but it will be a slow disaster if I continue like this.
How do i create one function for transposing to operate matrices of different sizes?
I suppose it can be done with pointers, but I don't know how.
I'm looking for a realy general answer to understand how to tune up the "comunication" between functions and temporary matrices, best with an example. Thank you all in advance for the information and help.
There are different way you can achieve this in c from not so good to good solutions.
If you know what the maximum size of the matrices would be you can create a matrix big enough to accommodate that size and work on it. If it is lesser than that - no problem write custom operations only considering that small sub-matrix rather than the whole one.
Another solution is to - create a data structure to hold the matrix this may vary from jagged array creation which can be done using the attribute that is stored in the structure itself. For example: number of rows and column information will be stored in the structure itself. Jagged array gives you the benefit that now you can allocate de-allocate memory - giving you a better control over the form - order of the matrices. This is better in that - now you can pass two matrices of different sizes and the functions all see that structure which contain the actual matrix and work on it. (wrapped I would say).
By Structure I meant something like
struct matrix{
int ** mat;
int row;
int col;
}
If your C implementation supports variable length arrays, then you can accomplish this with:
void matrix_transposition(size_t M, size_t N,
float Destination[M][N], const float Source[N][M])
{
for (size_t m = 0; m < M; ++m)
for (size_t n = 0; n < N; ++n)
Destination[m][n] = Source[n][m];
}
If your C implementation does not support variable length arrays, but does allow pointers to arrays to be cast to pointers to elements and used to access a two-dimensional array as if it were one-dimensional (this is not standard C but may be supported by a compiler), you can use:
void matrix_transposition(size_t M, size_t N,
float *Destination, const float *Source)
{
for (size_t m = 0; m < M; ++m)
for (size_t n = 0; n < N; ++n)
Destination[m*N+n] = Source[n*M+m];
}
The above requires the caller to cast the arguments to float *. We can make it more convenient for the caller with:
void matrix_transposition(size_t M, size_t N,
void *DestinationPointer, const void *SourcePointer)
{
float *Destination = DestinationPointer;
const float *Source = SourcePointer;
for (size_t m = 0; m < M; ++m)
for (size_t n = 0; n < N; ++n)
Destination[m*N+n] = Source[n*M+m];
}
(Unfortunately, this prevents the compiler from checking that the argument types match the intended types, but this is a shortcoming of C.)
If you need a solution strictly in standard C without variable length arrays, then, technically, the proper way is to copy the bytes of the objects:
void matrix_transposition(size_t M, size_t N,
void *DestinationPointer, const void *SourcePointer)
{
char *Destination = DestinationPointer;
const char *Source = SourcePointer;
for (size_t m = 0; m < M; ++m)
for (size_t n = 0; n < N; ++n)
{
// Calculate locations of elements in memory.
char *D = Destination + (m*N+n) * sizeof(float);
const char *S = Source + (n*M+m) * sizeof(float);
memcpy(D, S, sizeof(float));
}
}
Notes:
Include <stdlib.h> to declare size_t and, if using the last solution, include <string.h> to declare memcpy.
Variable length arrays were required in C 1999 but made optional in C 2011. Good quality compilers for general purpose systems will support them.
If you are using C99 compiler, you can make use of Variable Length Array (VLA's) (optional in C11 compiler). You can write a function like this:
void matrix_transposition (int rows_in_matrix, int columnes_in_matrix, float transposed_matrix[columnes_in_matrix][rows_in_matrix], float matrix[rows_in_matrix][columnes_in_matrix])
{
int i,j;
for(i = 0; i < rows_in_matrix; ++i) {
for(j = 0; j < columnes_in_matrix; ++j)
{
transposed_matrix[j][i] = matrix[i][j];
}
}
}
This one function can work for the different number of rows_in_matrix and columnes_in_matrix. Call it like this:
matrix_transposition (2, 4, transposed_matrix_A, input_matrix_A);
matrix_transposition (4, 4, transposed_matrix_B, input_matrix_B);
You probably don't want to be hard-coding array sizes in your program. I suggest a structure that contains a single flat array, which you can then interpret in two dimensions:
typedef struct {
size_t width;
size_t height;
float *elements;
} Matrix;
Initialize it with
int matrix_init(Matrix *m, size_t w, size_t h)
{
m.elements = malloc((sizeof *m.elements) * w * h);
if (!m.elements) {
m.width = m.height = 0;
return 0; /* failed */
}
m.width = w;
m.height = h;
return 1; /* success */
}
Then, to find the element at position (x,y), we can use a simple function:
float *matrix_element(Matrix *m, size_t x, size_t y)
{
/* optional: range checking here */
return m.elements + x + m.width * y;
}
This has better locality than an array of pointers (and is easier and faster to allocate and deallocate correctly), and is more flexible than an array of arrays (where, as you've found, the inner arrays need a compile-time constant size).
You might be able to use an array of arrays wrapped in a Matrix struct - it's possible you'll need a stride that is not necessarily the same as width, if the array of arrays has padding on your platform.

Is there a good reason why VLA are not permitted in pointers in structs?

Here is a way to define a Matrix type
typedef struct {
int nr, nc;
double *elem;
} Matrix;
I would like to define this
typedef struct {
int nr, nc;
double elem[nr][nc];
} Matrix;
It would be nice, because I would not have to worry about indexes. That's the reason VLA are useful in the first place, since they only do transparently what would be easy with index arithmetic.
Of course, the above is not possible, if only because the size of the struct would not be well defined. Then, I would still be happy with:
typedef struct {
int nr, nc;
double (*elem)[nc];
} Matrix;
Now, the matrix data is stored as a pointer, like in the non-VLA case. But the arithmetic could still be done by the compiler. The definition only tells it's some kind of pointer to double data, with the doubles arranged in an array of width nc.
It seems that it's not permitted either by the standard, and I wonder why, since it's easy to do the same by transtyping. For example, using the first definition (with double *), I could do
double get(Matrix *a, int i, int j) {
int nc = a->nc;
double (*p)[nc] = (double (*)[nc])a->elem;
return p[i][j];
}
Of course, it's not very interesting here, since there is only one access to elem, but it could be if there are many.
So, my question, with the hope that it's on topic: what's the very reason of prohibiting the third definition?
I could imagine that it's dangerous since it's not guaranteed that nc handles the correct value, but this is dangerous anyway with pointers, so it does not look like a good reason.
Does this meet your requirements? It stores a void * in the structure, and the access functions cast that to a pointer to a 2D VLA and use that. GCC 5.2.0 on Mac OS X 10.10.5 compiles it cleanly, and valgrind (3.11.0-SVN from November 2014 or thereabouts) gives it a clean bill of health.
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int nr, nc;
void *data; // Actually double a[nr][nc]
} Matrix;
static double get(Matrix *a, int i, int j)
{
double (*array)[a->nr][a->nc] = a->data;
return (*array)[i][j];
}
static void set(Matrix *a, int i, int j, double v)
{
double (*array)[a->nr][a->nc] = a->data;
(*array)[i][j] = v;
}
static Matrix *mat_alloc(int nr, int nc)
{
Matrix *m = malloc(sizeof(*m));
if (m != 0)
{
m->nr = nr;
m->nc = nc;
m->data = malloc(nr * nc * sizeof(double));
if (m->data == 0)
{
free(m);
m = 0;
}
}
return m;
}
static void mat_free(Matrix *m)
{
free(m->data);
free(m);
}
int main(void)
{
int nr = 3;
int nc = 5;
Matrix *m = mat_alloc(nr, nc);
if (m == 0)
{
fprintf(stderr, "Matrix allocation for %dx%d matrix failed\n", nr, nc);
exit(1);
}
for (int i = 0; i < nr; i++)
{
for (int j = 0; j < nc; j++)
{
double v = (i * (nc + 1)) + j + 1;
set(m, i, j, v);
printf("Set: [%d,%d] = %4.1f\n", i, j, v);
}
}
for (int j = 0; j < nc; j++)
{
for (int i = 0; i < nr; i++)
printf("Get: [%d,%d] = %4.1f\n", i, j, get(m, i, j));
}
mat_free(m);
return 0;
}
I'm not sure whether there's a neat way to lose the (*array) part of the notation in the access functions. I'd prefer it if there was one (other than using array[0][i][j], that is).
Example run
Set: [0,0] = 1.0
Set: [0,1] = 2.0
Set: [0,2] = 3.0
Set: [0,3] = 4.0
Set: [0,4] = 5.0
Set: [1,0] = 7.0
Set: [1,1] = 8.0
Set: [1,2] = 9.0
Set: [1,3] = 10.0
Set: [1,4] = 11.0
Set: [2,0] = 13.0
Set: [2,1] = 14.0
Set: [2,2] = 15.0
Set: [2,3] = 16.0
Set: [2,4] = 17.0
Get: [0,0] = 1.0
Get: [1,0] = 7.0
Get: [2,0] = 13.0
Get: [0,1] = 2.0
Get: [1,1] = 8.0
Get: [2,1] = 14.0
Get: [0,2] = 3.0
Get: [1,2] = 9.0
Get: [2,2] = 15.0
Get: [0,3] = 4.0
Get: [1,3] = 10.0
Get: [2,3] = 16.0
Get: [0,4] = 5.0
Get: [1,4] = 11.0
Get: [2,4] = 17.0
I believe that within a function in which a local variable nc is defined, you could use a typedef to create a local type double (*arr)[nc], and then cast a *double to that type. I believe such a cast would be legitimate for any *double that identifies a sufficiently-long sequence of double values, without regard for whether it was created using the same type as is defined within the function [if multiple functions each define their own array type, the compiler wouldn't recognize those types as equivalent, but it shouldn't matter]. I'm not 100% sure there wouldn't be Strict Aliasing issues, but I don't think there should be.
Otherwise, a fundamental difficulty is that a typedef involving a VLA creates a type using values that exist at a specific moment in time, and that can only occur for typedefs which are evaluated as executable statements, which in turn can only happen when typedefs are embedded within functions. Further, any identifiers used within array dimensions will be evaluated in the context of the enclosing function, rather than in the context of the partially-defined type.

c - static 2d array of pointers to 2d arrays

Is it possible to create statically in C a 2d array of pointers to 2d arrays, like:
#define m 4
#define n 4
#define p 2
#define q 2
char arr1[m][n] = {{0}};
char arr2[m][n] = {{0}};
char (*parr[m][n])[p][q] = {{&arr1, 0, &arr2, 0}};
int main() {
return 0;
}
The 2d array of pointers parr is sparse, with some values to 0 (NULL), which is why I don't want to use a 4d array in the first place.
This compiles, but I get the following warning:
warning: initialization from incompatible pointer type
With the following command:
gcc -Wall -Wextra -pedantic -std=c99 test.c
What is wrong?
The problem is that when you declare pointers, arrays and array pointers, the [] takes precedence over * unless you add parenthesis. This is why you declare an array pointer as (*parr)[] rather than *parr[], since the latter gives an array of pointers instead.
Similarly, when declaring a pointer to a 2D array, you would type (*parr)[m][n]. So it seems logical that an array of 2D-array pointers should be declared as ((*parr)[m][n])[p][q]). But the outer parenthesis here actually does nothing, that expression is equivalent to (*parr)[m][n][p][q]. And that's an array pointer to a 4D array! Which is why you get compiler warnings.
Now what you actually want is to get an array of array pointers to 2D arrays, is something like char (*parr[p][q])[m][n]. Which looks rather insane, nobody will understand a declaration like that.
The only sane way to write code like this is through typedefs:
typedef char arr_t[m][n];
arr_t arr1 = {{0}};
arr_t arr2 = {{0}};
arr_t* parr[p][q] =
{
{&arr1, 0},
{&arr2, 0}
};
I think what you meant to do is the following:
char arr1[m][n] = {{0}};
char arr2[m][n] = {{0}};
typedef char (*some_type)[n]; // type of arr1 and arr2
some_type parr[p][q] = {{arr1, NULL}, {arr2, NULL}}; //array containing arr1 and arr2
You can then access parr e.g by
printf("%c\n", parr[0][0][0][0]);
which will print arr1[0][0].
In the declaration of parr I think you meant to use p and q as its dimensions:
$ cat test.c
#include <stdio.h>
#define m 4
#define n 4
#define p 2
#define q 2
char arr1[m][n] = {{0}};
char arr2[m][n] = {{0}};
char (*parr[p][q])[m][n] = {{&arr1, NULL}, {&arr2, NULL}};
int main(void)
{
printf("%d\n", (*parr[0][0])[0][0]);
return 0;
}
This compiles cleanly with GCC 4.8.2:
$ gcc --version
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -ansi -fsanitize=address -g -pedantic -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -o test test.c
$
Here would be my solution -- i prefer using static 1D arrays rather than static 2D arrays due pointer arithmetic (i like to keep things simple):
PS. i would suggestin using m!=n and p!=q for testing, so you can catch possible indexing errors!
#include <stdio.h>
#define m 4
#define n 4
#define p 2
#define q 2
char arr1[m*n] = {0};
char arr2[m*n] = {0};
typedef char array_2d[m][n];
char* parr[p][q] = { {(char*)arr1, NULL}, { (char*)arr2, NULL} };
int main()
{
for (int i = 0; i < m; i ++ )
for (int j = 0; j < n; j ++ )
{
arr1[i*n + j] = i + j;
arr2[i*n + j] = i - j;
}
for (int i = 0; i < m; i ++ )
{
for (int j = 0; j < n; j ++ )
{
char* arr1_ptr = parr[0][0];
char* arr2_ptr = parr[1][0];
printf("%d", (int)(arr1_ptr[i*n + j] - arr2_ptr[i*n + j] ));
}
printf("\n");
}
return 0;
}

C function that takes const 2d array

I want to write a C function that takes a dynamic 2D array as an input, but doesn't alter the array.
I'm trying to be const correct, not only to make my code clearer, but because my functions are going to be called from within C++ code, and C++ is pretty persnickety about these things.
How do I declare a function to take 'const' pointer to a pointer, i.e. how do I indicate that the function will not alter the contents of the 2d array?
What follows is a specific, super-simple example. I'm using a 2D array of doubles, i.e. double**, to represent a square matrix in C of size n x n, and I want to write a function that computes the trace of one of these matrices:
#include <stdlib.h>
#include <stdio.h>
double **sqr_matrix_new(int n)
{
double **a = calloc(n, sizeof(double*));
int i;
for (i=0; i < n; ++i) a[i] = calloc(n, sizeof(double));
return a;
}
void sqr_matrix_free(double **a, int n)
{
int i;
for (i=0; i < n; ++i) free(a[i]);
free(a);
}
double sqr_matrix_trace(double **a, int n)
{
double trace;
int i;
for (i=0, trace=0.0; i < n; ++i) trace += a[i][i];
return trace;
}
double sqr_matrix_trace_const(const double * const *a, int n)
{
double trace;
int i;
for (i=0, trace=0.0; i < n; ++i) trace += a[i][i];
return trace;
}
int main(int argc, char *argv[])
{
int n = 10;
double **a = sqr_matrix_new(n);
int i, j, k;
for (i=0, k=0; i < n; ++i){
for (j=0; j < n; ++j) a[i][j] = k++;
}
printf("trace is %g\n", sqr_matrix_trace(a, n));
printf("trace is %g\n", sqr_matrix_trace_const(a, n));
printf("trace is %g\n", sqr_matrix_trace_const((const double * const *)a, n));
sqr_matrix_free(a, n);
}
In the above, both versions of the trace function, sqr_matrix_trace() and sqr_matrix_trace_const() compile cleanly (the latter is the one I prefer because it clearly demonstrates that there will be no alteration of the matrix it's given), but the call
sqr_matrix_trace_const(a, n)
produces the following warning:
sqr_matrix.c: In function 'main':
sqr_matrix.c:44: warning: passing argument 1 of 'sqr_matrix_trace_const' from incompatible pointer type
sqr_matrix.c:27: note: expected 'const double * const*' but argument is of type 'double **'
The cast overcomes this:
sqr_matrix_trace_const((const double * const *)a, n)
but it feels wrong to use a cast to use to overcome compiler inconveniences.
Alternatively, I could suppress the compiler warning, but that's a cop-out.
So, I want my code to compile cleanly and I want to convey the const-ness of a dynamic 2D array given to a function without resorting to a cast. It seems like a legitimate aim. Is this possible? If not, what's the standard/accepted practice for doing this?
The C const promotion rules don't allow promotion from T ** to const T const *. Per 6.5.16.1 1 (which applies to function calls as well as assignments per 6.5.2.2 2), conversion of pointers can only add qualifiers to the pointed-to type.
This is to prevent code like (example from 6.5.16.1 6):
const char **cpp;
char *p;
const char c = 'A';
cpp = &p; // constraint violation
*cpp = &c; // valid
*p = 0; // valid
It's correct to observe that const *const *cpp = &p is safe because then *cpp = &c is prevented, but this is a sufficiently obscure case that it's not covered in the standard.
Conclusion: you can and should cast to const double *const * yourself.
Note that it would be more efficient to use a single array of type double * with length n * n and do any necessary array indexing yourself: d[i][j] becomes d[i * n + j].
A C++ compiler would allow that.
As for C, qualified pointer types are not applied recursively.
If your matrix data truly is 2D and rectangular (without a "ragged right edge"), I don't see why you're not representing it as a single double * to the first element, together with integers giving width and height. This would allow you to both cut down on the number of allocations needed to initialize the matrix, but also make it representable as a plain old const double *.

Resources