0

I am wondering if there is reliable and standard-compliant way to copy only memberes from certain position in struct. For example something like this

struct A {
  char* baz;
  int foo;
  int bar;
};

void copy(struct A* dst, const struct A* src) {
  dst->baz = malloc(1 + strlen(src->baz));
  strcpy(dst->baz, src->baz);
  memcpy(
    ((void*)dst) + sizeof(char*),
    ((void*)src) + sizeof(char*),
    sizeof(struct A) - sizeof(char*)
  );
}

Is this valid C and does not violate the standard? I know there may be some issues with memory alignment sometimes, I don't know if it applies to this scenario.

Second question is - how to do it when skipping more than one member because the padding issues start to rear their ugly heads then

bartop
  • 9,971
  • 1
  • 23
  • 54
  • 1
    There's a few ideas for the offset here: [Finding offset of a structure element in C](https://stackoverflow.com/q/18749349/243245). It might be simpler to just copy the whole lot and overwrite elements though rather than selectively copying the bits you're not setting. – Rup Apr 09 '20 at 08:15
  • 3
    There is no reliable way to do such a thing. You can `memcpy` each member individually, or the whole struct. Everything else will be UB, because of padding. – flowit Apr 09 '20 at 08:17
  • 2
    @flowit If I use `offsetof` for start and `sizeof-offsetof` for length, would that be UB? – Gerhardh Apr 09 '20 at 08:30
  • Note that the result of `offsetof` is still compiler dependent, and does not work in all cases (for example not for bitfields). For your simple example it would actually work, but I recommend just copying the whole struct and replace the changed members. – flowit Apr 09 '20 at 08:39
  • 1
    @flowit The solution to that problem and many others, is to never use bit-fields. – Lundin Apr 09 '20 at 09:05
  • @flowit assuming no bitfields it would be possible? – bartop Apr 09 '20 at 11:20

1 Answers1

3
  • void copy(A* dst, const A* src) isn't valid C since you didn't typedef the struct. Change to typedef struct {...} A; if you wish to use this style.

  • strdup is not valid C (but valid POSIX). I wouldn't recommend to use it for any purpose. Use malloc + strcpy instead (or memcpy if the size is known).

  • dst + sizeof(char*) is nonsense since this does pointer arithmetic on whole structs. Same with src + sizeof(char*).

  • Fixing that bug, then there is still no guarantee that (uint8_t*)dst + sizeof(char*) gives the address of the member foo. The compiler is free to insert padding anywhere inside the struct (except before the very first member).

    You can reliably get the location of a specific member by offsetof(struct A, foo), which gives you the number of bytes from the start of the struct to that specific member.

The sane way to copy a specific member of a struct is otherwise dst->foo = src->foo;. There is no apparent reason why your code shouldn't be doing this.

Similarly, you could copy specific members only and set the rest of them to zero/NULL, by using a compound literal:

*dst = (A){ .foo = src->foo };
Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396