cunionsflexible-array-member

How to correctly use flexible array member?


Here is a type I declared:
(I declared t_sphere, t_cylinder and t_triangle too)

typedef struct  s_intersection{
  double       t1;
  double       t2;
  int id;
  union {
    t_sphere sph;
    t_cylinder cyl;
    t_triangle tri;
  } u[];
} t_intersection;

To refer to the union variable inside my struct, I have to know in advance what type it is. This is possible thanks to the id int declared in my struct, but it is very heavy to achieve with the method I thought of.
Here is the function I wrote to allocate a t_intersection variable:

t_intersection  *intersection(unsigned int geometric_figure_id, void *figure)
{
    t_intersection  *p;

    if (geometric_figure_id == SPHERE_ID)
        p = malloc(sizeof(*p) + sizeof (p->u[0].sp));
    else if (geometric_figure_id == CYLINDER_ID)
        p = malloc(sizeof(*p) + sizeof (p->u[0].cy));
    else if (geometric_figure_id == TRIANGLE_ID)
        p = malloc(sizeof(*p) + sizeof (p->u[0].tr));
    if (p == NULL)
        return (NULL);
    memset(&p, 0, sizeof(p));
    p->id = geometric_figure_id:
    if (geometric_figure_id == SPHERE_ID)
        p->u[0].sp = *(t_sphere *)figure;
    else if (geometric_figure_id == CYLINDER_ID)
        p->u[0].cy = *(t_cylinder *)figure;
    else if (geometric_figure_id == TRIANGLE_ID)
        p->u[0].tr = *(t_triangle *)figure;
    return (p);
}

Is there a better way of achieving this?
EDIT: I added the mallocs I forgot + assigning the id...


Solution

  • You need to always allocate space for the entire union, not just one member. Also, you can use calloc to allocate memory that is zero-initialized.

    So you can replace this:

    if (geometric_figure_id == SPHERE_ID)
        p = malloc(sizeof(*p) + sizeof (p->u[0].sp));
    else if (geometric_figure_id == CYLINDER_ID)
        p = malloc(sizeof(*p) + sizeof (p->u[0].cy));
    else if (geometric_figure_id == TRIANGLE_ID)
        p = malloc(sizeof(*p) + sizeof (p->u[0].tr));
    if (p == NULL)
        return (NULL);
    memset(&p, 0, sizeof(p));
    

    With this:

    p = calloc(1, sizeof(*p) + sizeof (p->u[0]));
    if (p == NULL)
        return (NULL);
    

    To simplify even more, since you're only every creating one instance of the union, you don't need a flexible array member. Just declare your struct like this:

    typedef struct  s_intersection{
      double       t1;
      double       t2;
      int id;
      union {
        t_sphere sph;
        t_cylinder cyl;
        t_triangle tri;
      };
    } t_intersection;
    

    And create an instance like this:

    t_intersection  *intersection(unsigned int geometric_figure_id, void *figure)
    {
        t_intersection  *p;
    
        p = calloc(1, sizeof(*p));
        if (p == NULL)
            return (NULL);
        p->id = geometric_figure_id:
        if (geometric_figure_id == SPHERE_ID)
            p->sp = *(t_sphere *)figure;
        else if (geometric_figure_id == CYLINDER_ID)
            p->cy = *(t_cylinder *)figure;
        else if (geometric_figure_id == TRIANGLE_ID)
            p->tr = *(t_triangle *)figure;
        return (p);
    }