1

In a project I'm working on, I find myself creating a lot of small, hand-written arrays of structs that need to be stored in dynamically allocated memory (mostly for testing with small datasets before importing from a file). There's a really nice syntax to initialize a static array of structs, as vec2 below, but I've been using the solution vec3 below that uses memcpy.

vec4 shows sort of what I'd like to be able to do, but obviously it doesn't quite work. Even though the anonymous array *((struct vec2d[]) { {1, 2}, {3, 4}, {5, 6} }) contains three strut vec2ds, when it (and the LHS) are dereferenced, they become (struct vec2d), so only the first struct vec2d gets assigned to the memory allocated at vec4.

Is there some way for me to accomplish this purely as an assignment, without using memcpy?

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

struct vec2d
{
  double x,y;
};

//void print_vec2d(char *, struct vec2d *);

void print_vec2ds(char *label, size_t count, struct vec2d *vec)
{
  for (size_t i = 0; i < count; i++)
  {
    printf("%s (%li of %li): (%f, %f)\n", label, i + 1, count, (vec[i]).x, (vec[i]).y);
  }

  printf("\n");
}

int 
main(int c, char **a)
{
  // nice and concise syntax
  struct vec2d vec2[3] =
  {
    {12, 34},
    {56, 78},
    {90, 112}
  };
  print_vec2ds("vec2", 3,  vec2);

  // a little bulky to have to wrap it all in a call to memcpy
  struct vec2d *vec3 = calloc(3, sizeof(struct vec2d));
  memcpy(vec3, (struct vec2d[])
  {
    {1, 2},
    {3, 4},
    {5, 6}
  }, 3*sizeof(struct vec2d));
  print_vec2ds("vec3", 3,  vec3);

  // this doesn't (and shouldn't) work, but I like the syntax :)
  struct vec2d *vec4 = calloc(3, sizeof(struct vec2d));
  *vec4 = *((struct vec2d[])
  {
    {1, 2},
    {3, 4},
    {5, 6}
  });
  print_vec2ds("vec4", 3,  vec4);
}

program output, showing how last assignment doesn't work:

vec2 (1 of 3): (12.000000, 34.000000)
vec2 (2 of 3): (56.000000, 78.000000)
vec2 (3 of 3): (90.000000, 112.000000)

vec3 (1 of 3): (1.000000, 2.000000)
vec3 (2 of 3): (3.000000, 4.000000)
vec3 (3 of 3): (5.000000, 6.000000)

vec4 (1 of 3): (1.000000, 2.000000)
vec4 (2 of 3): (0.000000, 0.000000)
vec4 (3 of 3): (0.000000, 0.000000)

Incidentally, I discovered that it's possible to cast the LHS and RHS of the assignment into the pointer type of a struct which holds the amount of data I want to assign:

struct bad {double a,b,c,d,e,f};
  struct vec2d *vec5 = calloc(3, sizeof(struct vec2d));
  *((struct bad*) vec5) = *((struct bad*) ((struct vec2d[])
  {
    {1, 2},
    {3, 4},
    {5, 6}
  }));
  print_vec2ds("vec5", 3,  vec5);

which "works", but it requires a struct of the precise length to be defined, which isn't really a solution…

catlover2
  • 251
  • 2
  • 15
  • Are the array sizes and values typically hard coded in practice? – Jeff Feb 27 '22 at 01:39
  • FYI, the RHS is called a "compound literal". – Barmar Feb 27 '22 at 01:42
  • In C, (almost) anything is possible. – Déjà vu Feb 27 '22 at 01:55
  • The only way you'll be able copy an array by assignment is by embedding the array in a structure (because that makes the array size explicit in all contexts). – user3386109 Feb 27 '22 at 02:04
  • catlover2, Try `*dest = *src;`. Convert the address of the source and destination to address of a `struct` containing the array 3 of `struct2d`. – chux - Reinstate Monica Feb 27 '22 at 02:13
  • @Jeff No, they're not. This question is really just about being able to embed a small handmade array into the source code for testing. – catlover2 Feb 27 '22 at 03:54
  • @chux-ReinstateMonica I think that's more or less equivalent to what I mentioned at the end of my post, where I define a new struct with 6 doubles in it, then cast the addresses to it. – catlover2 Feb 27 '22 at 03:55
  • 1
    A `struct` with 3 `struct vec2d` makes more sense than a `struct` with 6 `double`. Why introduce a new 6 `double` one? – chux - Reinstate Monica Feb 27 '22 at 05:58
  • @catlover2 Is there any issue with just declaring a static array outside of a function? Why does it need to be dynamically allocated? – Jeff Feb 27 '22 at 15:05

1 Answers1

3

Is there some way for me to accomplish this purely as an assignment, without using memcpy?

I believe that the size needs to be known at compile-time in order to do a pure assignment. Otherwise memcpy() is a fine solution. If you want to make it a bit simpler you could maybe do something like:

  struct vec2d vec2[3] =
  {
    {12, 34},
    {56, 78},
    {90, 112}
  };

  struct vec2d *vec3 = malloc(sizeof(vec2));
  memcpy(vec3, vec2, sizeof(vec2));
Jeff
  • 1,234
  • 8
  • 16