tpg2114
tpg2114

Reputation: 15100

Using structs to access arrays in C

I understand how to create a struct that can be used to access an array, for example:

#include <stdio.h>

typedef struct {
  int i;
  int j;
  int k;
} MyStruct;

int main()
{
  MyStruct *ms;
  int array[9];
  int i;

  for(i=0;i<9;++i) array[i] = i;

  ms = (MyStruct *)array;

  for(i=0;i<3;++i) printf("%d %d %d %d\n",i,ms[i].i,ms[i].j,ms[i].k);
  return 0;
}

which allows me to access array[0] as ms[1].i and array[1] as ms[1].j etc.. However, how could I do the same thing if I wanted to use an array in the struct that is known at runtime but not at compile time? Something like:

#include <stdio.h>

typedef struct {
  int *myArgs;
} MyStruct;

int main()
{
  MyStruct *ms;
  int array[9];
  int i;

  for(i=0;i<9;++i) array[i] = i;

  ms = (MyStruct *)array;

  for(i=0;i<3;++i) printf("%d %d %d %d\n",i,ms[i].myArgs[0],ms[i].myArgs[1],ms[i].myArgs[2]);
  return 0;
}

which in that example, I would know at runtime that myArgs is length 3. But it's also possible myArgs is length 9, in which case ms should be length 1.

Edit:

I'm storing a solution set for a PDE solver that has a number of fixed-at-compile-time knowns and then an unknown number of extras. For example, my array will have X, Y, Z but then it might have 3 or 5 or 10 more things after that. So, I wanted to provide a structure that gives access to the fields for easy readability later, for example solution.x, solution.y, solution.z, solution.chemicalSpecies[0], solution.chemicalSpecies[1], etc.. The number of species is entirely unknown at compile time.

I'm using an outside library that stores the number of unknowns as an array, and I can get the array back in the form [k][j][i][numUnknowns] and it would be nice to give access like solution[k][j][i].something.

Upvotes: 2

Views: 209

Answers (3)

lvella
lvella

Reputation: 13413

No.

In your first case, the size of your structure is the size of 3 int variables. When you cast the pointer of your array to the pointer of your structure, each 3 int elements will match with the one MyStruct element, what does the magic in your code (that is theoretically non-portable).

In your second case, the size of the structure is the size of 1 pointer to int. You must be aware that pointers have some similarities with arrays regarding usage, but they are not the same thing. Pointers are just one variable with a single value that points to a memory address. The contents of that address can then be used as an array.

In C, structs and arrays sizes are dealt at compile time, so a struct's size cannot change at runtime, thus it will do no good to your purpuse to have a pointer inside your struct, since pointer sizes are also fixed.

Maybe a good solution to you would be to use an abstract data type, like:

typedef struct {
  int m; /* number of lines */
  int n; /* number of columns */
  int *data; /* pointer to the actual data */
} Matrix;

Then you could create ancillary functions for using the struct:

void matrix_new(Matrix *m, int rows, int columns);
int matrix_get(Matrix *m, int i, int j);
void matrix_set(Matrix *m, int i, int j, int value);
void matrix_free(Matix *m);

If that is too clumsy, you could even use a mixed approach (not too "abstract" type):

int matrix_get(Matrix *m, int i, int j)
{
  return m->data[i * m->n + j];
}

int main()
{
  Matrix m;
  int array[9];
  int i;

  for(i=0;i<9;++i) array[i] = i;

  m.m = m.n = 3;
  m.data = array;

  for(i=0;i<3;++i)
    printf("%d %d %d %d\n", i,
                            matrix_get(&m, i, 0),
                            matrix_get(&m, i, 1),
                            matrix_get(&m, i, 2));
  return 0;
}

Upvotes: 1

Dan Fego
Dan Fego

Reputation: 13994

Given your clarification, I would go about the problem as follows. I'd have a struct that had both the fixed x, y, and z, and then a pointer to an array, which you could set to the location of the appropriate point in the solution array. Something like this:

struct MyStruct {
    int x, y, z;
    int *extras;
};

...
int solutions[9];
struct MyStruct ms;
ms.x = solutions[0];
ms.y = solutions[1];
ms.z = solutions[2];
ms.extras = (solutions + 3);
...

That way, you could easily access the fields you need, and extras like:

ms.extras[0]

At least that should work, off the top of my head.

I'd also throw in a num_extras into the struct if I were you, just so you don't run off the end of the array.

Edit: If you were looking to have them be mirrored, you could instead do:

struct MyStruct {
    int *x, *y, *z;
    int *extras;
};

And then have:

...
int solutions[9];
struct MyStruct ms;
ms.x = &solutions[0];
ms.y = &solutions[1];
ms.z = &solutions[2];
ms.extras = (solutions + 3);
...

But that's a lot like what you had before, isn't it?

Upvotes: 0

AShelly
AShelly

Reputation: 35520

Trivially, given array size N and myArgs < N, you will have N/myArgs complete sets of data, with a remainder of N%myArgs items. You could base your array indexing on that fact.

However, this code looks like a maintenence nightmare. What are you trying to accomplish? There is probably a better way.

Upvotes: 1

Related Questions