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

6
  • 1
    There's a few ideas for the offset here: Finding offset of a structure element in C. It might be simpler to just copy the whole lot and overwrite elements though rather than selectively copying the bits you're not setting. Commented Apr 9, 2020 at 8: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. Commented Apr 9, 2020 at 8:17
  • 2
    @flowit If I use offsetof for start and sizeof-offsetof for length, would that be UB? Commented Apr 9, 2020 at 8: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. Commented Apr 9, 2020 at 8:39
  • 1
    @flowit The solution to that problem and many others, is to never use bit-fields. Commented Apr 9, 2020 at 9:05

1 Answer 1

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 };
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.