Dynamically set array access pattern in C

2019-07-22 04:28发布

I would like to do something like this in C(99):

int n = compute_size_of_matrices();
int T = compute_number_of_matrices();
float matrices[][n][n] = malloc( sizeof(float) * n*n* T ); //[sic!]

Watch the missing size in the first dimension. I would like to set the size of the dimension dynamically in order to do something like the following

/* Load each matrix with the unit matrix */
for( int t = 0; t < T; t++ )
for( int i = 0; i < n; i++ )
for( int j = 0; j < n; j++ )
    matrices[t][i][j] = (i==j)?1.:0.;

Of course, I could simply allocate 'matrices' as a one-dimensional array and use some index magic, but obviously the above access pattern to the array is way more convenient. In particular, it is no technical black magic for the compiler, except some overhead to remember the dynamic array sizes. Is there a compiler extension of GCC, some dialect feature or similar which allows me to do something like above?

In contrast to this, and in order to avoid ambiguity, a usual C code for the above task would look like this:

int n = compute_size_of_matrices();
int T = compute_number_of_matrices();
float* matrices = malloc( sizeof(float) * n*n* T ); //[sic!]
for( int t = 0; t < T; t++ )
for( int i = 0; i < n; i++ )
for( int j = 0; j < n; j++ )
    matrices[t*n*n + i*n + j] = (i==j)?1.:0.;

In real examples, the one-letter variables might have more sounding names, which quickly renders such a piece of code clumsy.

2条回答
干净又极端
2楼-- · 2019-07-22 04:44

RE: missing size in the first direction

The reason you can omit the first dimension in array is because compiler can figure out the size from the number of values that you pass during initialization.

example:

int arr[] = { 0,1 };             // arr size is 2
int arr[][2] = { {0,1}, {0,1} }; // arr size is 2,
                                 // each element points to an array of size 2

etc ...

Another case:

float * arr = calloc(float, sizeof(float) * n);

Here you allocated continuous memory for n floats and arr points to it. You can access elements by pointer arithmetic or by array subscript which does the pointer arithmetic for you.

In all cases the size of each dimension of an array is determined once the array is defined (on the next line after the declaration or once the pointer points to the actual memory).

Compiler does not remember the size, if you use array subscripts that go outside of the array you are accessing memory which is not part of an array ...

查看更多
贪生不怕死
3楼-- · 2019-07-22 04:48

Best I've found

Preserves the array size attributes of the matricies (though not of the list of matrices), and lets you use the native indexing notation

  int n=5, T=2;
  float (*a)[n][n] = calloc(T,sizeof(float[n][n])); // <== initializes to 0!
  for (size_t t=0; t<T; ++t) 
    for (size_t i=0; i<n; ++i)
      a[t][i][i] = 1.0;   // <== only set the 1's

The declaration of the pointer to the 2d arrays is the tricky part, but calloc (see below) takes care of the zero initialization for you, so you only set the non-zero elements.

Of course, the fun part comes when you try to pass these things around...but if you are careful with your declaration and are using c99 you can make either of

void foo(int n, float (*a)[n][n]) {
  // ...
}

void bar(int t, int n, float a[t][n][n]) {
  // ...
}

work. (Actually gcc will let you get away with unless you use -std=c89 -pendantic...)

Second best, but it will work with ansi-c

You can certainly make the traditional version (with ugly hand indexing) easier to read.

int n = compute_size_of_matrices();
int T = compute_number_of_matrices();
float* matrices = calloc(T,  sizeof(float) * n*n); // <== initializes to 0!
for( int t = 0; t < T; t++ )
   for( int i = 0; i < n; i++ )
    matrices[t*n*n + i*n + i] = 1;  // <== only set the 1's

Well, it seemed like a good idea...

Alas c won't let you do

  int n=5, T=2;
  float matrices[T][n][n] = {};  // <== ***ERROR!!!***

which would let you keep the "arrayness" of matricies and be even clearer.

Is it slow?

Because calloc will use some heavily optimized system memory writer to set to 0 you won't be taking a big speed hit.

查看更多
登录 后发表回答