1

I am trying to use an array of pointers to a struct that has a pointer in it to another struct that is being used with an array of pointers.

I can write the code for a two single structs and it works. However, I am not sure how to deal with the introduction of the array of pointers. Sample code:

typedef struct {
    double vx;
} particle_t;
    
typedef struct {
    int m;
    int n;
    particle_t *inparticles;    // Pointer to particles inside grid
} grid_t;
    
particle_t *particles = (particle_t*) malloc( 3 * sizeof(particle_t) );
grid_t *grid = (grid_t*) malloc( 3 * sizeof(grid_t) ); // trying 3 pointers

if( particles==NULL || grid==NULL ){
    puts("Unable to allocate memory");
    exit(1);}

// fill the structure 
grid[1].inparticles = particles[1]; // I am not sure how to link these
grid[2].inparticles = particles[2]; // these lines give incompatible types error
grid[3].inparticles = particles[3];
grid[1].m = 5;
grid[1].n = 3;
grid[1].inparticles -> vx = 15;

Thank you in advance!

Update:

I realized that what I should be doing is use the inparticles pointer to point to multiple particles in the same grid pointer? How I can change the code so I can be able to do that? for example:

grid[0].inparticles[0] = particles[0];
grid[0].inparticles[1] = particles[1];
grid[0].inparticles[2] = particles[2];

declare it as an array somehow? would I need memory allocation for it as well?

Yaser Baqi
  • 155
  • 1
  • 12

5 Answers5

1

Each member of particles has type particle_t. The inparticles member of grid_t has type particle_t *. So you want:

grid[1].inparticles = &particles[1];
grid[2].inparticles = &particles[2];
grid[3].inparticles = &particles[3];

Addressing your update, if you want to have inparticles point to various members of particles, you would need to change the inparticles to be a pointer-to-pointer:

typedef struct {
    int m;
    int n;
    particle_t **inparticles;
} grid_t;

Then you would have to allocate space for an array of pointers, then assign each pointer:

grid[0].inparticles = malloc(3 * sizeof(particle_t *));
grid[0].inparticles[0] = &particles[0];
grid[0].inparticles[1] = &particles[1];
grid[0].inparticles[2] = &particles[2];
dbush
  • 205,898
  • 23
  • 218
  • 273
1

Issues

Design

I am trying to use an array of pointers to a struct that has a pointer in it to another struct that is being used with an array of pointers.

In other words, we must find a way to design "An array of pointers to a struct, which contains a pointer to another struct used as an array".

I will focus on this.

Array of pointers

However, I am not sure how to deal with the introduction of the array of pointers.

Well, we don't have any array of pointers in your code... you must create one first.

C pointers, arrays, structures and all their combinations have countless threads all over the internet which have been answered several times. I will show you a few basic examples so you can be up and running, just don't expect me to give you a whole lesson of pointers.


Solutions

Design

Here is the code (I changed some variable names to make it more readable):

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    double value;
}Particle;

typedef struct
{
    int width;
    int height;
    Particle *particles;
}Grid;

Grid *createGrid(int width, int height)
{
    Grid *result = malloc(sizeof(Grid));
    if (result == NULL)
    {
        printf("Error while allocating memory for the grid structure!\n");
        exit(1);
    }else
    {
        result->width = width;
        result->height = height;
        result->particles = malloc(sizeof(Particle) * width*height);
    }
    return result;
}

void destroyGrid(Grid *grid)
{
    free(grid->particles);
    free(grid);
}

int main()
{
    Grid *grids[3];
    for (int i = 0; i < 3; i++)
        grids[i] = createGrid(5, 3);
    
    /* Assign, modify, read or copy values from the array of pointers here */
    
    for (int i = 0; i < 3; i++)
        destroyGrid(grids[i]);
    return 0;
}

As you can see, I introduced 2 functions to make your life easier: createGrid and destroyGrid.

The first one will need you to specify 2 arguments: the width and height (m and n in your code) of a grid. The function is then going to allocate memory and return a pointer to the grid it just created.

The second one will need you to specify 1 argument: a pointer to a grid. The function is then going to free all the memory the first function allocated, avoiding any memory leaks.

I've also added an array of pointers in the main function: grids. This, in combination with the usage of the functions described above (all in the main function), solves the first issue.

The final design can be read as follows:
"We have an array of pointers to a struct (grids), which contains a pointer to another struct (particles) which should be used as an array".

Array of pointers

You may be wondering now: "OK, so how am I supposed to use this array-of-pointers-to-a-structure-confusing-stuff?" As I said before, I will show you a practical example only.

Lets assume you want to set the value of the third particle of the first grid to 2.7182: (you should insert this code in the place I commented as /* Assign, modify, read or copy values from the array of pointers here */)

grids[0]->particles[2].value = 2.7182;

And this can be read, from left to right, as: "From the array of pointers grids, get the element with index 0 (the first pointer to a Grid structure). Access its structure member particles, which is a pointer. Treat that pointer as an array and get the element with index 2 (the third Particle structure). Access its member value and assign to it the value 2.7182"

You could then do something like copy one particle to another:

grids[1]->particles[1] = grids[0]->particles[2];

Here are some links to better understand pointers, arrays and structs. Practice and experimentation is the best way to deal with them:


Update

I realized that what I should be doing is use the inparticles pointer to point to multiple particles in the same grid pointer

I need to use a separate array of pointers to particles. The array inside the grid structure is only to point to a subset of particles.

Objective: "Use the inparticles pointer to point to a subset of multiple (sequential or non-sequential?) particles (which are being managed somewhere else)".

If you want the sequential solution you could use a pointer, representing a pointer to the base address (or offset, if you want only a subset) of an array (this is, a pointer to an array). This has the advantage of not needing you to allocate extra memory for every single pointer (since only the base address is necessary), but it has the disadvantage that you can only access particles that are next to each other.

Example:

typedef struct
{
    int width;
    int height;
    Particle *subparticles; //Represents a pointer to an array subset
}Grid;
    /*...*/

    grids[0]->subparticles = &particles[0];
    grids[1]->subparticles = &particles[1];

    printf("%f equals %f", grids[0]->subparticles[1].value, grids[1]->subparticles[0].value);

    /*...*/

However, If you want the non-sequential solution, you could use a double pointer (representing an array of pointers) while having to manually allocate memory for the array. This provides you with more flexibility, but comes at the price of memory allocation overhead.

typedef struct
{
    /*...other members...*/
    Particle **subparticles;
}Grid;

Grid *createGrid(int subparticlesCount)
{
    Grid *result = malloc(sizeof(Grid));
    if (result == NULL)
    {
        printf("Error while allocating memory for the grid structure!\n");
        exit(1);
    }else
    {
        result->subparticles = malloc(sizeof(Particle *) * subparticlesCount);
    }
    return result;
}
    /*...*/

    grids[0]->subparticles[0] = &particles[1];
    grids[0]->subparticles[1] = &particles[5];
    grids[0]->subparticles[2] = &particles[3];
    grids[1]->subparticles[0] = &particles[3];

    printf("%f equals %f", grids[0]->subparticles[2]->value, grids[1]->subparticles[0]->value);

    /*...*/
1

Instead of a field which is a pointer (to some array), if that field is the last of its struct, you could make it a flexible array member. So declare

typedef struct {
  int m;
  int n;
  unsigned count;
  particle_t inparticle_arr[];    // flexible array of count elements
} grid_t;

See this answer for an example and details, and a C standard (like n1570 §6.7.2.1) or this C reference.

In some cases, avoiding pointers and indirection may slightly speedup your code (because of the CPU cache). Flexible array members also avoid an extra free for that internal flexible array.

For debugging and avoiding buffer overflows, compile your C code with GCC invoked as gcc -Wall -Wextra -g and use also valgrind (or the address sanitizer).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

Based on the question and subsequent comments, what I understood is that you want to create a grid which should contain a pointer to an array of pointers (i.e multiple pointers) in which each pointer points to struct of type particle_t.

On the basis of this understanding, I have modified your original code and have provided appropriate comments in the code itself wherever necessary. However, if I have misinterpreted your question then let me know, I will update the answer accordingly.

#include<stdio.h>
#include<stdlib.h>

typedef struct {
    double vx;
} particle_t;

typedef struct {
    int m;
    int n;
    particle_t **inparticles;    // inparticles is a pointer to (array of 
                                // pointers to inparticle)
    int size; // max number of particles in the grid
} grid_t;

grid_t *new_grid(int size) { // A helper function to create grid
    grid_t *grid = (grid_t*)malloc(sizeof(grid_t));
    grid->inparticles = (particle_t**)malloc(sizeof(particle_t*));

    if(grid == NULL || grid->inparticles == NULL) return NULL;

    grid->size = size;

    for(int i=0;i<size;++i) {
        grid->inparticles[i] = (particle_t*)malloc(sizeof(particle_t));
        if(grid->inparticles[i] == NULL) {
            return NULL;
        }
    }

    return grid;
}
int main() {
    particle_t *particles = (particle_t*)malloc( 3 * sizeof(particle_t) );

    //create a new grid which can contain multiple particles --- Use a for 
    //loop for creating multiple grids 
    grid_t *grid = new_grid(3);

    if( particles==NULL || grid==NULL ){
        puts("Unable to allocate memory");
        exit(1);
    }

    grid->inparticles[0] = &particles[0];
    grid->inparticles[1] = &particles[1];
    grid->inparticles[2] = &particles[2];

    return 0;
}
Abhi
  • 116
  • 7
1

When things get complicated, divide and conquer is the best approach.

Create an array of structs of type particle_t on the heap:

particle_t* partAddr = malloc(sizeof(particle_t) * numParticles);

Create an array of structs of type grid_t on the heap:

grid_t* gridAddr = malloc(sizeof(grid_t) * numGrids);

Make one element of the array of type grid_t to point to an allocated element of type particle_t:

gridAddr[n].inparticles = partAddr;

Assign a value to the vx field of the first element of the array of the first element of the grid:

grid[0].inparticles[0].vx= 3;

Everything together (where the number of particles can vary):

// Define the size of each array of particles
#define ELEMENTS_ARRAY_PARTICLE_A 5
#define ELEMENTS_ARRAY_PARTICLE_B 2
#define ELEMENTS_ARRAY_PARTICLE_C 7

// Create indexes and size to avoid out of boundaries accesses
enum GRID_PARTICLES {
    ARRAY_PARTICLE_0 = 0,
    ARRAY_PARTICLE_1,
    ARRAY_PARTICLE_2,
    GRID_SIZE
};

typedef struct {
    double vx;
} particle_t;

typedef struct {
    particle_t *inparticles;
} grid_t;

particle_t* allocateArrayOfParticles(int numParticles) {
    particle_t* partAddr = malloc(sizeof(particle_t) * numParticles);
    if(partAddr == NULL){
        // trigger an error
    }
    return partAddr;
}

void deallocateArrayOfParticles(particle_t *partAddr) {
    free(partAddr);
}


grid_t* allocateGridOfParticles(int numGrids) {
    grid_t* gridAddr = malloc(sizeof(grid_t) * numGrids);
    if(gridAddr == NULL) {
        // trigger an error
    }
    return gridAddr;
}

void deallocateGridOfParticles(grid_t *gridAddr) {
    free(gridAddr);
}

int main(void) {    
    grid_t *grid = allocateGridOfParticles(GRID_SIZE);
    particle_t *partA = allocateArrayOfParticles(ELEMENTS_ARRAY_PARTICLE_A);
    particle_t *partB = allocateArrayOfParticles(ELEMENTS_ARRAY_PARTICLE_B);
    particle_t *partC = allocateArrayOfParticles(ELEMENTS_ARRAY_PARTICLE_C);
  
    grid[ARRAY_PARTICLE_0].inparticles = partA;
    grid[ARRAY_PARTICLE_1].inparticles = partB;
    grid[ARRAY_PARTICLE_2].inparticles = partC;

    deallocateArrayOfParticles(partA);
    deallocateArrayOfParticles(partB);
    deallocateArrayOfParticles(partC);
    deallocateGridOfParticles(grid);
}
Jose
  • 3,306
  • 1
  • 17
  • 22