2

I have two objects both of the same type, for instance:

typedef struct s_Object 
{
  signed int a[3]; 
  double float b;
  unsigned short int c[30];
  struct s_Object *next, 
                  *prev;
} t_Object;
t_Object A, B;

I need some generic function like:

swap_vars(&A, &B);

which will not swap pointers but all other data the objects can contain. Is it possible in C?

Assuming pointers are placed in the highest addresses, my first idea was:

void swap_vars(t_Object *pA, t_Object *pB){
   t_Object C;
   memcpy(&C, pA, sizeof(t_Object)-sizeof(t_Object *)*2;
   memcpy(pA, pB, sizeof(t_Object)-sizeof(t_Object *)*2;
   memcpy(pB, &C, sizeof(t_Object)-sizeof(t_Object *)*2;
}

but I don't know how portable is it (or maybe wrong at all)?

I am looking for a most portable solution.

psihodelia
  • 29,566
  • 35
  • 108
  • 157
  • 1
    You shouldn't assume anything about the layout of the struct, just memcopy the individual members using their names – Martin Beckett Mar 23 '11 at 13:46
  • @Martin: It's not a beautiful solution, because each time I will change object's type description, I have to update this function as well. – psihodelia Mar 23 '11 at 13:50

1 Answers1

3

Use offsetof:

memcpy(&C, pA, offsetof(t_Object, next));

The reason is that t_Object might have padding at the end, so if you copy all but the last 8 bytes, you might be copying part or all of the pointers.

I'm not sure whether this is strictly conforming - the standard says you can copy a whole object, but I'm not sure what it says about parts of objects.

It would be a bit strange, but imagine that this struct had some padding in the middle, and some at the end, and the implementation filled all the padding in each object with the same byte value, chosen at random, and later checked to see that the padding was still consistent. I don't think that's forbidden. Then this wouldn't work.

In practice, though, this is pretty portable. In particular, members have to appear in the order they're defined, so offsetof(t_Object, next) certainly captures the part of the object up to (but not including) the pointers.

If you're able to change the structure, then you could build it up from a swapped part, and an unswapped part:

typedef struct payload {
    signed int a[3]; 
    double float b;
    unsigned short int c[30];
} payload;

typedef struct s_Object {
    payload data;
    struct s_Object *next, *prev;
} t_Object;

Swap is then:

payload C = pA->data;
pA->data = pB->data;
pB->data = C;

Edit: prompted by memcpy vs assignment in C -- should be memmove?

This latter also has the advantage that it's guaranteed by the standard to work when pA == pB. The one with memcpy isn't, because for the second copy the regions overlap. Neither is optimal for the case of a self-swap, but generally speaking those are probably so rare that it isn't worth checking the pointers if you don't have to.

Community
  • 1
  • 1
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Thanks, good idea to use payload! It seems I have seen something similar already somewhere (either git or python sources). – psihodelia Mar 23 '11 at 14:06